Comment Social Preview (#4260)
* Add route for comment social preview * Add method comment to social_previews_controller * Add view for comment social preview * Fix spacing in meta keywords * Update comments page keywords * Create helper for comment social media image * Use comment social image, add published condition for article social image * Add tests for comment social preview * Remove not_found fallback for social previews controller * Remove cached_tag_list * Update typos of word 'image' in spec
This commit is contained in:
parent
328d27ff41
commit
8b4f7faf6b
7 changed files with 203 additions and 11 deletions
|
|
@ -14,24 +14,30 @@ class SocialPreviewsController < ApplicationController
|
|||
end
|
||||
|
||||
def user
|
||||
@user = User.find(params[:id]) || not_found
|
||||
@user = User.find(params[:id])
|
||||
set_respond
|
||||
end
|
||||
|
||||
def listing
|
||||
@listing = ClassifiedListing.find(params[:id]) || not_found
|
||||
@listing = ClassifiedListing.find(params[:id])
|
||||
define_categories
|
||||
set_respond
|
||||
end
|
||||
|
||||
def organization
|
||||
@user = Organization.find(params[:id]) || not_found
|
||||
@user = Organization.find(params[:id])
|
||||
|
||||
set_respond "user"
|
||||
end
|
||||
|
||||
def tag
|
||||
@tag = Tag.find(params[:id]) || not_found
|
||||
@tag = Tag.find(params[:id])
|
||||
|
||||
set_respond
|
||||
end
|
||||
|
||||
def comment
|
||||
@comment = Comment.find(params[:id])
|
||||
|
||||
set_respond
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ module SocialImageHelper
|
|||
Articles::SocialImage.new(article).url
|
||||
end
|
||||
|
||||
def comment_social_image_url(comment)
|
||||
comment_social_preview_url(comment, format: :png)
|
||||
end
|
||||
|
||||
def use_new_social_url?(resource)
|
||||
resource.updated_at > SOCIAL_PREVIEW_MIGRATION_DATETIME
|
||||
end
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
<link rel="canonical" href="https://dev.to<%= @article.path %>" />
|
||||
<% end %>
|
||||
<meta name="description" content="<%= @article.description || "An article from the community" %>">
|
||||
<meta name="keywords" content="software development, inclusive, community,engineering,<%= @article.cached_tag_list %>">
|
||||
<meta name="keywords" content="software development, inclusive, community, engineering, <%= @article.cached_tag_list %>">
|
||||
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="https://dev.to<%= @article.path %>" />
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<%= content_for :page_meta do %>
|
||||
<meta name="description" content="<%= @commentable.description || "An article from the community" %>">
|
||||
<meta name="keywords" content="software development,engineering,rails,javascript,ruby">
|
||||
<meta name="keywords" content="software development, inclusive, community, engineering">
|
||||
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:title" content="Discussion of <%= @commentable.title %>" />
|
||||
|
|
@ -16,20 +16,23 @@
|
|||
<meta name="twitter:creator" content="@<%= @user.twitter_username %>">
|
||||
<meta name="twitter:title" content="<%= @commentable.title %>">
|
||||
<meta name="twitter:description" content="<%= @commentable.description || "A DEV Comment" %>">
|
||||
|
||||
<% if @root_comment.present? %>
|
||||
<link rel="canonical" href="https://dev.to<%= @root_comment.path %>" />
|
||||
<meta property="og:url" content="https://dev.to<%= @root_comment.path %>" />
|
||||
<meta property="og:title" content="<%= truncate(strip_tags(@root_comment.processed_html), length: 50) %> — DEV" />
|
||||
<meta name="twitter:title" content="<%= truncate(strip_tags(@root_comment.processed_html), length: 50) %> — DEV">
|
||||
<meta property="og:image" content="<%= comment_social_image_url(@root_comment) %>" />
|
||||
<meta name="twitter:image:src" content="<%= comment_social_image_url(@root_comment) %>">
|
||||
<% else %>
|
||||
<link rel="canonical" href="https://dev.to<%= @commentable.path %>/comments" />
|
||||
<meta property="og:url" content="https://dev.to<%= @commentable.path %>/comments" />
|
||||
<meta property="og:title" content="[Discussion] <%= @commentable.title %> — DEV" />
|
||||
<meta name="twitter:title" content="[Discussion] <%= @commentable.title %> — DEV">
|
||||
<% end %>
|
||||
<% if @commentable.class.name == "Article" %>
|
||||
<meta property="og:image" content="<%= article_social_image_url(@commentable) %>" />
|
||||
<meta name="twitter:image:src" content="<%= article_social_image_url(@commentable) %>">
|
||||
<% if @commentable.class.name == "Article" && @commentable.published %>
|
||||
<meta property="og:image" content="<%= article_social_image_url(@commentable) %>" />
|
||||
<meta name="twitter:image:src" content="<%= article_social_image_url(@commentable) %>">
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
|
|
|||
149
app/views/social_previews/comment.html.erb
Normal file
149
app/views/social_previews/comment.html.erb
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<% accent_color = HexComparer.new([user_colors(@comment.user)[:bg], user_colors(@comment.user)[:text]]).biggest %>
|
||||
<% color = HexComparer.new([user_colors(@comment.user)[:bg], user_colors(@comment.user)[:text]]).brightness(1.4) %>
|
||||
<% dark_color = HexComparer.new([user_colors(@comment.user)[:bg], user_colors(@comment.user)[:text]]).brightness(0.7) %>
|
||||
<% not_so_rand = Random.new(@comment.id) # Using ID as seed ensures we get the same "random" numbers on each page load, Improves caching %>
|
||||
<style>
|
||||
body {
|
||||
background: white;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.preview-div-wrapper {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.circle {
|
||||
border-radius: 1000px;
|
||||
background: <%= accent_color %>;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.circle1 {
|
||||
height: 40vw;
|
||||
width: 40vw;
|
||||
right: -<%= not_so_rand.rand(2..15) %>vw;
|
||||
bottom: -<%= not_so_rand.rand(2..15) %>vw;
|
||||
}
|
||||
|
||||
.circle2 {
|
||||
height: 30vw;
|
||||
width: 30vw;
|
||||
left: <%= not_so_rand.rand(2..16) %>vw;
|
||||
bottom: -<%= not_so_rand.rand(2..15) %>vw;
|
||||
}
|
||||
|
||||
.circle3 {
|
||||
height: 24vw;
|
||||
width: 24vw;
|
||||
left: -8vw;
|
||||
top: -<%= not_so_rand.rand(2..6) %>vw;
|
||||
}
|
||||
|
||||
.preview-div {
|
||||
background: #fff;
|
||||
border: 0.25vw solid<%= dark_color %>;
|
||||
box-shadow: 0.8vw 1vw 0px<%= dark_color %>;
|
||||
width: 88vw;
|
||||
height: 44vw;
|
||||
margin: 2.5vw auto 5vw;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.preview-info-header {
|
||||
color: black;
|
||||
margin: 2vw auto 0vw;
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, "Roboto", monospace;
|
||||
font-size: 3.5vw;
|
||||
width: 92%;
|
||||
}
|
||||
|
||||
.preview-info-header img {
|
||||
height: 2.5vw;
|
||||
vertical-align: -0.15vw;
|
||||
margin-left: 1vw;
|
||||
margin-right: 0.5vw;
|
||||
}
|
||||
|
||||
.preview-comment {
|
||||
margin: 3vw auto;
|
||||
}
|
||||
|
||||
.preview-comment h1 {
|
||||
color: <%= dark_color %>;
|
||||
width: 92%;
|
||||
margin: 1vw auto;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", "Roboto", sans-serif;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: <%= dark_color %>;
|
||||
width: 92%;
|
||||
margin: 1vw auto;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", "Roboto", sans-serif;
|
||||
font-size: 3vw;
|
||||
}
|
||||
|
||||
.preview-user {
|
||||
font-size: 3.5vw;
|
||||
position: absolute;
|
||||
background: <%= accent_color %>;
|
||||
left: 1.5vw;
|
||||
bottom: 1.5vw;
|
||||
padding: 1vw 3vw;
|
||||
padding-left: 1.2vw;
|
||||
font-weight: 500;
|
||||
border-radius: 100vw;
|
||||
}
|
||||
|
||||
.preview-user img {
|
||||
border-radius: 100vw;
|
||||
height: 5vw;
|
||||
width: 5vw;
|
||||
vertical-align: -1.3vw;
|
||||
border: 0.25vw solid<%= dark_color %>;
|
||||
}
|
||||
|
||||
.preview-dev-logo {
|
||||
position: absolute;
|
||||
bottom: 2.1vw;
|
||||
right: 2.1vw;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6vw;
|
||||
fill: <%= dark_color %>;
|
||||
}
|
||||
</style>
|
||||
<div class="preview-div-wrapper">
|
||||
<div class="circle circle1"></div>
|
||||
<div class="circle circle2"></div>
|
||||
<div class="circle circle3"></div>
|
||||
<div class="preview-div">
|
||||
<% font_size = 12 - (((@comment.commentable.title.size**0.89) + 85) / 16) %>
|
||||
<% if @comment.commentable.tag_list.include?("discuss") %>
|
||||
<% font_size = 10.5 - (((@comment.commentable.title.size**0.89) + 85) / 16) %>
|
||||
<div class="preview-info-header">
|
||||
<% if @comment.commentable.comments_count > 0 %>
|
||||
#discuss <img src="<%= asset_path "comments-bubble.png" %>" /><%= @comment.commentable.comments_count %>
|
||||
<% else %>
|
||||
New Discussion!
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<h2>re: <%= @comment.commentable.title %></h2>
|
||||
<div class="preview-comment">
|
||||
<h1 style="font-size:<%= font_size %>vw;"><%= @comment.title %></h1>
|
||||
</div>
|
||||
<div class="preview-user">
|
||||
<img src="<%= ProfileImage.new(@comment.user).get(90) %>" />
|
||||
<%= truncate @comment.user.name, length: 25 %>・<%= @comment.readable_publish_date %>
|
||||
</div>
|
||||
<div class="preview-dev-logo">
|
||||
<%= inline_svg("devplain.svg", class: "logo", size: "9vw * 9vw", aria: false, title: "DEV logo") %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -210,6 +210,7 @@ Rails.application.routes.draw do
|
|||
get "/social_previews/organization/:id" => "social_previews#organization", as: :organization_social_preview
|
||||
get "/social_previews/tag/:id" => "social_previews#tag", as: :tag_social_preview
|
||||
get "/social_previews/listing/:id" => "social_previews#listing", as: :listing_social_preview
|
||||
get "/social_previews/comment/:id" => "social_previews#comment", as: :comment_social_preview
|
||||
|
||||
get "/async_info/base_data", controller: "async_info#base_data", defaults: { format: :json }
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ RSpec.describe "SocialPreviews", type: :request do
|
|||
let(:tag) { create(:tag) }
|
||||
let(:organization) { create(:organization) }
|
||||
let(:article) { create(:article, user_id: user.id) }
|
||||
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
|
||||
let(:image_url) { "https://hcti.io/v1/image/6c52de9d-4d37-4008-80f8-67155589e1a1" }
|
||||
let(:listing) { create(:classified_listing, user_id: user.id, category: "cfp") }
|
||||
|
||||
|
|
@ -136,9 +137,37 @@ RSpec.describe "SocialPreviews", type: :request do
|
|||
expect(first_request_body).to eq second_request_body
|
||||
end
|
||||
|
||||
it "renders and image when requested and redirects to iamge url" do
|
||||
it "renders and image when requested and redirects to image url" do
|
||||
get "/social_previews/listing/#{listing.id}.png"
|
||||
expect(response).to redirect_to(image_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /social_previews/comment/:id" do
|
||||
it "renders proper comment name" do
|
||||
get "/social_previews/comment/#{comment.id}"
|
||||
expect(response.body).to include CGI.escapeHTML(comment.title)
|
||||
end
|
||||
|
||||
it "renders associated article name" do
|
||||
get "/social_previews/comment/#{comment.id}"
|
||||
expect(response.body).to include CGI.escapeHTML(comment.commentable.title)
|
||||
end
|
||||
|
||||
it "renders consistent HTML between requests" do
|
||||
# We use the HTML for caching. It needs to be deterministic (if data is unchanged, the HTML should be the same)
|
||||
get "/social_previews/comment/#{comment.id}"
|
||||
first_request_body = response.body
|
||||
|
||||
get "/social_previews/comment/#{comment.id}"
|
||||
second_request_body = response.body
|
||||
|
||||
expect(first_request_body).to eq second_request_body
|
||||
end
|
||||
|
||||
it "renders and image when requested and redirects to image url" do
|
||||
get "/social_previews/comment/#{comment.id}.png"
|
||||
expect(response).to redirect_to(image_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue