Show localized date/time on hover for articles/comments (#2722)
* Add article decorator published_timestamp * Use time HTML5 element and refactor date in partial * Add published_timestamp to Article Adding `published_timestamp` to the homepage we can then use JS to render the full timestamp localized for the user. We've also added the timestamp to the index and the API * Display article published timestamp on hover * Use time also in the article show page * Add timestamp to bottom articles as well * Remove published_timestamp from index because it is not used * Fix broken specs * Add more article dates specs * Refactor date initializers
This commit is contained in:
parent
260fe901b8
commit
4e591fea1c
25 changed files with 211 additions and 101 deletions
|
|
@ -28,6 +28,7 @@ function callInitalizers(){
|
|||
initializeCommentsPage();
|
||||
initEditorResize();
|
||||
initLeaveEditorWarning();
|
||||
initializeArticleDate();
|
||||
initializeArticleReactions();
|
||||
initNotifications();
|
||||
initializeSplitTestTracking();
|
||||
|
|
|
|||
10
app/assets/javascripts/initializers/initializeArticleDate.js
Normal file
10
app/assets/javascripts/initializers/initializeArticleDate.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* Show article date/time according to user's locale */
|
||||
/* global addLocalizedDateTimeToElementsTitles */
|
||||
|
||||
function initializeArticleDate() {
|
||||
var articlesDates = document.querySelectorAll(
|
||||
'.single-article time, article time, .single-other-article time',
|
||||
);
|
||||
|
||||
addLocalizedDateTimeToElementsTitles(articlesDates, 'datetime');
|
||||
}
|
||||
|
|
@ -1,50 +1,8 @@
|
|||
/* Show comment date/time according to user's locale */
|
||||
/* global timestampToLocalDateTime */
|
||||
/* global addLocalizedDateTimeToElementsTitles */
|
||||
|
||||
function initializeCommentDate() {
|
||||
// example: "Wednesday, April 3, 2019, 2:55:14 PM"
|
||||
var hoverTimeOptions = {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
};
|
||||
var commentsDates = document.querySelectorAll('.comment-date time');
|
||||
|
||||
// example: "Apr 3"
|
||||
var visibleDateOptions = {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
};
|
||||
|
||||
var commentDates = document.getElementsByClassName('comment-date');
|
||||
for (var i = 0; i < commentDates.length; i += 1) {
|
||||
// get UTC timestamp set by the server
|
||||
var ts = commentDates[i].getAttribute('data-published-timestamp');
|
||||
|
||||
// add a full datetime to the comment date string, visible on hover
|
||||
// `navigator.language` is used for full date times to allow the hover date
|
||||
// to be localized according to the user's locale
|
||||
var hoverTime = timestampToLocalDateTime(
|
||||
ts,
|
||||
navigator.language,
|
||||
hoverTimeOptions,
|
||||
);
|
||||
commentDates[i].setAttribute('title', hoverTime);
|
||||
|
||||
// replace the comment short visible date with the equivalent localized one
|
||||
var visibleDate = commentDates[i].querySelector('a');
|
||||
if (visibleDate) {
|
||||
var localVisibleDate = timestampToLocalDateTime(
|
||||
ts,
|
||||
'en-US', // en-US because for now we want all users to see `Apr 3`
|
||||
visibleDateOptions,
|
||||
);
|
||||
if (localVisibleDate) {
|
||||
visibleDate.innerHTML = localVisibleDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
addLocalizedDateTimeToElementsTitles(commentsDates, 'datetime');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,11 @@ function buildArticleHTML(article) {
|
|||
|
||||
var publishDate = '';
|
||||
if (article.readable_publish_date) {
|
||||
publishDate = '・'+article.readable_publish_date
|
||||
if (article.published_timestamp) {
|
||||
publishDate = '・' + '<time datetime="'+article.published_timestamp+'">'+article.readable_publish_date+'</time>';
|
||||
} else {
|
||||
publishDate = '・' + '<time>'+article.readable_publish_date+'</time>';
|
||||
}
|
||||
}
|
||||
var readingTimeHTML = '';
|
||||
if (article.reading_time && article.class_name === "Article") {
|
||||
|
|
|
|||
|
|
@ -43,8 +43,12 @@ function buildCommentHTML(comment) {
|
|||
</a>\
|
||||
'+twitterIcon+'\
|
||||
'+githubIcon+'\
|
||||
<div class="comment-date" data-published-timestamp="' + comment.published_timestamp + '">\
|
||||
<a href="'+comment.url+'">'+comment.readable_publish_date+'</a>\
|
||||
<div class="comment-date">\
|
||||
<a href="'+comment.url+'">\
|
||||
<time datetime="'+comment.published_timestamp+'">\
|
||||
'+comment.readable_publish_date+'\
|
||||
</time>\
|
||||
</a>\
|
||||
</div>\
|
||||
<button class="dropbtn">\
|
||||
<%= image_tag("three-dots.svg", class: "dropdown-icon", alt: "Toggle dropdown menu") %>\
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
/* Local date/time utilities */
|
||||
|
||||
/*
|
||||
Convert string timestamp to local time, using the given locale.
|
||||
|
||||
|
|
@ -20,3 +22,36 @@ function timestampToLocalDateTime(timestamp, locale, options) {
|
|||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function addLocalizedDateTimeToElementsTitles(elements, timestampAttribute) {
|
||||
// example: "Wednesday, April 3, 2019, 2:55:14 PM"
|
||||
var timeOptions = {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
};
|
||||
|
||||
for (var i = 0; i < elements.length; i += 1) {
|
||||
var element = elements[i];
|
||||
|
||||
// get UTC timestamp set by the server
|
||||
var timestamp = element.getAttribute(timestampAttribute || 'datetime');
|
||||
|
||||
if (timestamp) {
|
||||
// add a full datetime to the element title, visible on hover.
|
||||
// `navigator.language` is used to allow the date to be localized
|
||||
// according to the browser's locale
|
||||
// see <https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/language>
|
||||
var localDateTime = timestampToLocalDateTime(
|
||||
timestamp,
|
||||
navigator.language,
|
||||
timeOptions,
|
||||
);
|
||||
element.setAttribute('title', localDateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -690,7 +690,7 @@ a.header-link {
|
|||
.comment-date {
|
||||
border: none;
|
||||
position: absolute;
|
||||
top: calc(16px - 0.25vw);
|
||||
top: calc(14px - 0.25vw);
|
||||
right: calc(35px + 0.2vw);
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class Article < ApplicationRecord
|
|||
}
|
||||
|
||||
scope :limited_column_select, lambda {
|
||||
select(:path, :title, :id,
|
||||
select(:path, :title, :id, :published,
|
||||
:comments_count, :positive_reactions_count, :cached_tag_list,
|
||||
:main_image, :main_image_background_hex_color, :updated_at, :slug,
|
||||
:video, :user_id, :organization_id, :video_source_url, :video_code,
|
||||
|
|
@ -131,10 +131,7 @@ class Article < ApplicationRecord
|
|||
|
||||
algoliasearch per_environment: true, auto_remove: false, enqueue: :trigger_delayed_index do
|
||||
attribute :title
|
||||
add_index "searchables",
|
||||
id: :index_id,
|
||||
per_environment: true,
|
||||
enqueue: :trigger_delayed_index do
|
||||
add_index "searchables", id: :index_id, per_environment: true, enqueue: :trigger_delayed_index do
|
||||
attributes :title, :tag_list, :main_image, :id, :reading_time, :score,
|
||||
:featured, :published, :published_at, :featured_number,
|
||||
:comments_count, :reactions_count, :positive_reactions_count,
|
||||
|
|
@ -163,10 +160,7 @@ class Article < ApplicationRecord
|
|||
customRanking ["desc(search_score)", "desc(hotness_score)"]
|
||||
end
|
||||
|
||||
add_index "ordered_articles",
|
||||
id: :index_id,
|
||||
per_environment: true,
|
||||
enqueue: :trigger_delayed_index do
|
||||
add_index "ordered_articles", id: :index_id, per_environment: true, enqueue: :trigger_delayed_index do
|
||||
attributes :title, :path, :class_name, :comments_count, :reading_time, :language,
|
||||
:tag_list, :positive_reactions_count, :id, :hotness_score, :score, :readable_publish_date, :flare_tag, :user_id,
|
||||
:organization_id, :cloudinary_video_url, :video_duration_in_minutes, :experience_level_rating, :experience_level_rating_distribution
|
||||
|
|
@ -385,6 +379,13 @@ class Article < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def published_timestamp
|
||||
return "" unless published
|
||||
return "" unless crossposted_at || published_at
|
||||
|
||||
(crossposted_at || published_at).utc.iso8601
|
||||
end
|
||||
|
||||
def self.seo_boostable(tag = nil, time_ago = 18.days.ago)
|
||||
time_ago = 5.days.ago if time_ago == "latest" # Time ago sometimes returns this phrase instead of a date
|
||||
time_ago = 75.days.ago if time_ago.nil? # Time ago sometimes is given as nil and should then be the default. I know, sloppy.
|
||||
|
|
@ -539,7 +540,6 @@ class Article < ApplicationRecord
|
|||
end
|
||||
|
||||
def update_cached_user
|
||||
cached_org_object = nil
|
||||
if organization
|
||||
cached_org_object = {
|
||||
name: organization.name,
|
||||
|
|
@ -550,7 +550,7 @@ class Article < ApplicationRecord
|
|||
}
|
||||
self.cached_organization = OpenStruct.new(cached_org_object)
|
||||
end
|
||||
cached_user_object = nil
|
||||
|
||||
if user
|
||||
cached_user_object = {
|
||||
name: user.name,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ json.array! @articles do |article|
|
|||
json.id article.id
|
||||
json.title article.title
|
||||
json.description article.description
|
||||
json.cover_image cloud_cover_url article.main_image
|
||||
json.cover_image cloud_cover_url(article.main_image)
|
||||
json.published_at article.published_at
|
||||
json.tag_list article.cached_tag_list_array
|
||||
json.slug article.slug
|
||||
|
|
@ -12,6 +12,7 @@ json.array! @articles do |article|
|
|||
json.canonical_url article.processed_canonical_url
|
||||
json.comments_count article.comments_count
|
||||
json.positive_reactions_count article.positive_reactions_count
|
||||
json.published_timestamp article.published_timestamp
|
||||
|
||||
json.user do
|
||||
json.name article.user.name
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ json.url @article.url
|
|||
json.canonical_url @article.processed_canonical_url
|
||||
json.comments_count @article.comments_count
|
||||
json.positive_reactions_count @article.positive_reactions_count
|
||||
json.published_timestamp @article.published_timestamp
|
||||
|
||||
json.body_html @article.processed_html
|
||||
json.ltag_style(@article.liquid_tags_used.map { |ltag| Rails.application.assets["ltags/#{ltag}.css"].to_s.html_safe })
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@
|
|||
<div class="content">
|
||||
<h3><%= article.title %></h3>
|
||||
<h4>
|
||||
<%= article.user.name %>
|
||||
<span class="published-at">- <%= article.readable_publish_date if article.published_at %></span>
|
||||
<%= article.user.name %> - <time datetime="<%= article.published_timestamp %>"><%= article.readable_publish_date %></time>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@
|
|||
</div>
|
||||
</a>
|
||||
<h4>
|
||||
<a href="/<%= story.cached_user.username %>"><%= story.cached_user.name %>・<%= story.readable_publish_date %><span class="time-ago-indicator-initial-placeholder" data-seconds="<%= story.published_at_int %>"></span></a>
|
||||
|
||||
<a href="/<%= story.cached_user.username %>">
|
||||
<%= story.cached_user.name %>・<time datetime="<%= story.published_timestamp %>"><%= story.readable_publish_date %></time>
|
||||
<span class="time-ago-indicator-initial-placeholder" data-seconds="<%= story.published_at_int %>"></span>
|
||||
</a>
|
||||
</h4>
|
||||
<div class="tags">
|
||||
<% story.cached_tag_list_array.each do |tag| %>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,9 @@
|
|||
data-algolia-tag=""
|
||||
data-feed="<%= params[:timeframe] || "base-feed" %>"
|
||||
data-articles-since="<%= Timeframer.new(params[:timeframe]).datetime.to_i %>">
|
||||
|
||||
<%= render "articles/sidebar" %>
|
||||
|
||||
<div class="articles-list" id="articles-list">
|
||||
<div class="on-page-nav-controls" id="on-page-nav-controls">
|
||||
<div class="on-page-nav-label">
|
||||
|
|
@ -80,10 +82,12 @@
|
|||
<img src="<%= asset_path "lightning.svg" %>" alt="right-sidebar-nav">
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<% if @home_page %>
|
||||
<% @featured_story ||= @stories.where.not(main_image: nil).first&.decorate || Article.new %>
|
||||
<% @stories = @stories.decorate %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @featured_story.id %>
|
||||
<div id="featured-story-marker" data-featured-article="articles-<%= @featured_story.id %>"></div>
|
||||
<img src="<%= cloud_cover_url(@featured_story.main_image) %>" style="display:none" alt="<%= @featured_story.title %>" />
|
||||
|
|
@ -98,8 +102,12 @@
|
|||
<a href="/<%= @featured_story.cached_user.username %>" class="featured-profile-button">
|
||||
<img class="featured-profile-pic" src="<%= ProfileImage.new(@featured_story.cached_user).get(90) %>" alt="<%= @featured_story.title %>" />
|
||||
</a>
|
||||
<div class="featured-user-name"><a href="/<%= @featured_story.cached_user.username %>"><%= @featured_story.cached_user.name %>
|
||||
・<%= @featured_story.readable_publish_date %><span class="time-ago-indicator-initial-placeholder" data-seconds="<%= @featured_story.published_at_int %>"></span></a></div>
|
||||
<div class="featured-user-name">
|
||||
<a href="/<%= @featured_story.cached_user.username %>">
|
||||
<%= @featured_story.cached_user.name %>・<time datetime="<%= @featured_story.published_timestamp %>"><%= @featured_story.readable_publish_date %></time>
|
||||
<span class="time-ago-indicator-initial-placeholder" data-seconds="<%= @featured_story.published_at_int %>"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="featured-tags tags">
|
||||
<% @featured_story.cached_tag_list_array.each do |tag| %>
|
||||
<a href="/t/<%= tag %>"><span class="tag">#<%= tag %></span></a>
|
||||
|
|
@ -136,16 +144,20 @@
|
|||
</div>
|
||||
</a>
|
||||
<% end %>
|
||||
|
||||
<div id="article-index-podcast-div"></div>
|
||||
|
||||
<div class="substories" id="substories">
|
||||
<% if @stories.any? %>
|
||||
<%= render "stories/main_stories_feed" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="loading-articles" id="loading-articles">
|
||||
loading...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render "articles/sidebar_additional" %>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -127,7 +127,9 @@
|
|||
<% if @user.github_username.present? %>
|
||||
<a href="http://github.com/<%= @user.github_username %>"><%= image_tag_or_inline_svg "github" %></a>
|
||||
<% end %>
|
||||
<span class="published-at" itemprop="datePublished"><%= @article.readable_publish_date if @article.published_at %></span>
|
||||
<% if @article.published_timestamp.present? %>
|
||||
<time itemprop="datePublished" datetime="<%= @article.published_timestamp %>"><%= @article.readable_publish_date %></time>
|
||||
<% end %>
|
||||
<% if @second_user.present? %>
|
||||
<em>with <b><a href="<%= @second_user.path %>"><%= @second_user.name %></a></b></em>
|
||||
<% end %>
|
||||
|
|
@ -135,14 +137,16 @@
|
|||
<em> and <b><a href="<%= @third_user.path %>"><%= @third_user.name %></a></b></em>
|
||||
<% end %>
|
||||
<% if should_show_updated_on?(@article) %>
|
||||
<span class="published-at updated-at"><em>Updated on <span itemprop="dateModified"><%= @article.edited_at&.strftime("%b %d, %Y") %></span> </em></span>
|
||||
<span><em>Updated on <time itemprop="dateModified" datetime="<%= @article.edited_at&.utc&.iso8601 %>"><%= @article.edited_at&.strftime("%b %d, %Y") %></time></em></span>
|
||||
<% elsif should_show_crossposted_on?(@article) %>
|
||||
<span class="published-at updated-at">
|
||||
<span>
|
||||
<em>
|
||||
Originally published at
|
||||
<a href="<%= @article.canonical_url || @article.feed_source_url %>" style="color:#1395b8"><%= get_host_without_www(@article.canonical_url || @article.feed_source_url) %></a>
|
||||
on
|
||||
<span class="posted-date-inline"><%= (@article.originally_published_at || @article.published_at).strftime("%b %d, %Y") if @article.crossposted_at %></span>
|
||||
<% if @article.crossposted_at %>
|
||||
<time datetime="<%= (@article.originally_published_at || @article.published_at)&.utc&.iso8601 %>"><%= (@article.originally_published_at || @article.published_at)&.strftime("%b %d, %Y") %></time>
|
||||
<% end %>
|
||||
</em>
|
||||
</span>
|
||||
<% end %>
|
||||
|
|
|
|||
7
app/views/comments/_comment_date.erb
Normal file
7
app/views/comments/_comment_date.erb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<div class="comment-date">
|
||||
<a href="<%= decorated_comment.path %>">
|
||||
<time datetime="<%= decorated_comment.published_timestamp %>">
|
||||
<%= decorated_comment.readable_publish_date %>
|
||||
</time>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -47,9 +47,7 @@
|
|||
<% if commentable_author_is_op?(commentable, comment) %>
|
||||
<span class="op-marker"><%= get_ama_or_op_banner(commentable) %></span>
|
||||
<% end %>
|
||||
<div class="comment-date" data-published-timestamp="<%= decorated_comment.published_timestamp %>">
|
||||
<a href="<%= comment.path %>"><%= comment.readable_publish_date %></a>
|
||||
</div>
|
||||
<%= render "comments/comment_date", decorated_comment: decorated_comment %>
|
||||
<button class="dropbtn" aria-label="Toggle dropdown menu">
|
||||
<%= image_tag("three-dots.svg", class: "dropdown-icon", alt: "Dropdown menu icon") %>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,22 @@
|
|||
<div class="liquid-comment">
|
||||
<div class="details">
|
||||
<a href="/<%= comment.user.username %>">
|
||||
<img class="profile-pic" src="<%= ProfileImage.new(comment.user).get(50) %>" alt="<%= comment.user.username %> profile image"/>
|
||||
<img class="profile-pic" src="<%= ProfileImage.new(comment.user).get(50) %>" alt="<%= comment.user.username %> profile image" />
|
||||
</a>
|
||||
<a href="/<%= comment.user.username %>">
|
||||
<span class="comment-username"><%= comment.user.name %></span>
|
||||
</a>
|
||||
<% if comment.user.twitter_username.present? %>
|
||||
<a href="https://twitter.com/<%= comment.user.twitter_username %>">
|
||||
<img src="/assets/twitter-logo.svg" class="icon-img" alt="twitter"/>
|
||||
<img src="/assets/twitter-logo.svg" class="icon-img" alt="twitter" />
|
||||
</a>
|
||||
<% end %>
|
||||
<% if comment.user.github_username.present? %>
|
||||
<a href="https://github.com/<%= comment.user.github_username %>">
|
||||
<img src="/assets/github-logo.svg" class="icon-img" alt="github"/>
|
||||
<img src="/assets/github-logo.svg" class="icon-img" alt="github" />
|
||||
</a>
|
||||
<% end %>
|
||||
<div class="comment-date" data-published-timestamp="<%= comment.decorate.published_timestamp %>">
|
||||
<a href="<%= comment.path %>"><%= comment.readable_publish_date %></a>
|
||||
</div>
|
||||
<%= render "comments/comment_date", decorated_comment: comment.decorate %>
|
||||
</div>
|
||||
<div class="body">
|
||||
<%= comment.processed_html.html_safe %>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
reading_time video_thumbnail_url video video_duration_in_minutes language
|
||||
experience_level_rating experience_level_rating_distribution cached_user cached_organization],
|
||||
methods: %i[readable_publish_date cached_tag_list_array flare_tag class_name
|
||||
cloudinary_video_url video_duration_in_minutes published_at_int],
|
||||
cloudinary_video_url video_duration_in_minutes published_at_int
|
||||
published_timestamp],
|
||||
) %>">
|
||||
</div>
|
||||
<div id="home-articles-object" data-articles="
|
||||
|
|
@ -23,7 +24,8 @@
|
|||
reading_time video_thumbnail_url video video_duration_in_minutes language
|
||||
experience_level_rating experience_level_rating_distribution cached_user cached_organization],
|
||||
methods: %i[readable_publish_date cached_tag_list_array flare_tag class_name
|
||||
cloudinary_video_url video_duration_in_minutes published_at_int],
|
||||
cloudinary_video_url video_duration_in_minutes published_at_int
|
||||
published_timestamp],
|
||||
) %>">
|
||||
<% 3.times do %>
|
||||
<div class="single-article single-article-small-pic">
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@
|
|||
<a href="<%= comment.path %>">
|
||||
<div class="single-comment <%= "strong-comment" if comment.ancestry.nil? %>">
|
||||
<span class="comment-title">re: <%= comment.commentable.title %></span>
|
||||
<span class="comment-date" data-published-timestamp="<%= comment.decorate.published_timestamp %>"><%= comment.readable_publish_date %></span>
|
||||
<span class="comment-date">
|
||||
<time datetime="<%= comment.decorate.published_timestamp %>"><%= comment.readable_publish_date %></time>
|
||||
</span>
|
||||
<div class="comment-preview"><%= truncate(strip_tags(comment.processed_html), length: 64).html_safe %></div>
|
||||
</div>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -507,5 +507,31 @@ RSpec.describe Article, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe "published_timestamp" do
|
||||
it "returns empty string if the article is new" do
|
||||
expect(Article.new.published_timestamp).to eq("")
|
||||
end
|
||||
|
||||
it "returns empty string if the article is not published" do
|
||||
article.update_column(:published, false)
|
||||
expect(article.published_timestamp).to eq("")
|
||||
end
|
||||
|
||||
it "returns the timestamp of the crossposting date over the publishing date" do
|
||||
crossposted_at = 1.week.ago
|
||||
published_at = 1.day.ago
|
||||
article.update_columns(
|
||||
published: true, crossposted_at: crossposted_at, published_at: published_at,
|
||||
)
|
||||
expect(article.published_timestamp).to eq(crossposted_at.utc.iso8601)
|
||||
end
|
||||
|
||||
it "returns the timestamp of the publishing date if there is no crossposting date" do
|
||||
published_at = 1.day.ago
|
||||
article.update_columns(published: true, crossposted_at: nil, published_at: published_at)
|
||||
expect(article.published_timestamp).to eq(published_at.utc.iso8601)
|
||||
end
|
||||
end
|
||||
|
||||
include_examples "#sync_reactions_count", :article
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ RSpec.describe "Views an article", type: :system do
|
|||
let(:dir) { "../../support/fixtures/sample_article.txt" }
|
||||
let(:template) { File.read(File.join(File.dirname(__FILE__), dir)) }
|
||||
let!(:article) do
|
||||
create(:article,
|
||||
user_id: user.id,
|
||||
body_markdown: template.gsub("false", "true"),
|
||||
body_html: "")
|
||||
create(:article, user_id: user.id, body_markdown: template.gsub("false", "true"), body_html: "")
|
||||
end
|
||||
let!(:timestamp) { "2019-03-04T10:00:00Z" }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
|
|
@ -25,4 +23,21 @@ RSpec.describe "Views an article", type: :system do
|
|||
visit "/#{user.username}/#{article.slug}"
|
||||
expect(page).to have_selector(".single-comment-node", visible: true, count: 3)
|
||||
end
|
||||
|
||||
context "when showing the date" do
|
||||
before do
|
||||
article.update_column(:published_at, Time.zone.parse(timestamp))
|
||||
end
|
||||
|
||||
it "shows the readable publish date" do
|
||||
visit "/#{user.username}/#{article.slug}"
|
||||
expect(page).to have_selector("article time", text: "Mar 4")
|
||||
end
|
||||
|
||||
it "embeds the published timestamp" do
|
||||
visit "/#{user.username}/#{article.slug}"
|
||||
selector = "article time[datetime='#{timestamp}']"
|
||||
expect(page).to have_selector(selector)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ RSpec.describe "Viewing a comment", type: :system, js: true do
|
|||
|
||||
context "when showing the date" do
|
||||
it "shows the readable publish date" do
|
||||
expect(page).to have_selector(".comment-date", text: "Mar 4")
|
||||
expect(page).to have_selector(".comment-date time", text: "Mar 4")
|
||||
end
|
||||
|
||||
it "embeds the published timestamp" do
|
||||
selector = ".comment-date[data-published-timestamp='#{timestamp}']"
|
||||
selector = ".comment-date time[datetime='#{timestamp}']"
|
||||
expect(page).to have_selector(selector)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,21 +5,52 @@ RSpec.describe "User visits a homepage", type: :system do
|
|||
let!(:article2) { create(:article, reactions_count: 20, featured: true) }
|
||||
let!(:bad_article) { create(:article, reactions_count: 0) }
|
||||
let!(:user) { create(:user) }
|
||||
let!(:timestamp) { "2019-03-04T10:00:00Z" }
|
||||
|
||||
context "when no options specified" do
|
||||
before { visit "/" }
|
||||
context "when main featured article" do
|
||||
before do
|
||||
article.update_column(:published_at, Time.zone.parse(timestamp))
|
||||
article2.update_column(:published_at, Time.zone.parse(timestamp))
|
||||
visit "/"
|
||||
end
|
||||
|
||||
it "shows the main article" do
|
||||
expect(page).to have_selector(".big-article", visible: true)
|
||||
it "shows the main article" do
|
||||
expect(page).to have_selector(".big-article", visible: true)
|
||||
end
|
||||
|
||||
it "shows the main article readable date" do
|
||||
expect(page).to have_selector(".big-article time", text: "Mar 4")
|
||||
end
|
||||
|
||||
it "embeds the main article published timestamp" do
|
||||
selector = ".big-article time[datetime='#{timestamp}']"
|
||||
expect(page).to have_selector(selector)
|
||||
end
|
||||
end
|
||||
|
||||
it "shows correct articles" do
|
||||
article.update_column(:score, 15)
|
||||
article2.update_column(:score, 15)
|
||||
expect(page).to have_selector(".single-article", count: 2)
|
||||
expect(page).to have_text(article.title)
|
||||
expect(page).to have_text(article2.title)
|
||||
expect(page).not_to have_text(bad_article.title)
|
||||
context "when all other articles" do
|
||||
before do
|
||||
article.update_columns(score: 15, published_at: Time.zone.parse(timestamp))
|
||||
article2.update_columns(score: 15, published_at: Time.zone.parse(timestamp))
|
||||
visit "/"
|
||||
end
|
||||
|
||||
it "shows correct articles" do
|
||||
expect(page).to have_selector(".single-article", count: 2)
|
||||
expect(page).to have_text(article.title)
|
||||
expect(page).to have_text(article2.title)
|
||||
expect(page).not_to have_text(bad_article.title)
|
||||
end
|
||||
|
||||
it "shows all articles dates" do
|
||||
expect(page).to have_selector(".single-article time", text: "Mar 4", count: 2)
|
||||
end
|
||||
|
||||
it "embeds all articles published timestamps" do
|
||||
selector = ".single-article time[datetime='#{timestamp}']"
|
||||
expect(page).to have_selector(selector, count: 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ RSpec.describe "User index", type: :system do
|
|||
it "embeds comment timestamp" do
|
||||
within("#substories .index-comments .single-comment") do
|
||||
ts = comment.decorate.published_timestamp
|
||||
timestamp_selector = ".comment-date[data-published-timestamp='#{ts}']"
|
||||
timestamp_selector = ".comment-date time[datetime='#{ts}']"
|
||||
expect(page).to have_selector(timestamp_selector)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue