[deploy] Add index and show pages for series (#9201)

* Add index and show pages for series

* Address code review

* Use `id` instead of `slug` to get collections

* Extract `collection_link` into the application helper

* Only count published articles

* Get rid of the `collection_link_class` method

* Add tests

* Fix test

* Use `**kwargs` instead of `options = {}`
This commit is contained in:
Alberto Pérez de Rada Fiol 2020-07-29 16:01:52 +02:00 committed by GitHub
parent 8b882dd512
commit 115adbb1bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 174 additions and 23 deletions

View file

@ -319,10 +319,6 @@ article {
color: var(--body-color-inverted);
}
&.coming-soon {
pointer-events: none;
color: lighten($black, 78%);
}
&.collection-link-hidden {
display: none;
}

View file

@ -568,10 +568,6 @@
color: var(--body-color-inverted);
}
&.coming-soon {
pointer-events: none;
color: var(--base-80);
}
&.collection-link-hidden {
display: none;
}

View file

@ -112,7 +112,8 @@ class ArticlesController < ApplicationController
def create
authorize Article
article = Articles::Creator.call(current_user, article_params_json)
@user = current_user
article = Articles::Creator.call(@user, article_params_json)
render json: if article.persisted?
{ id: article.id, current_state_path: article.decorate.current_state_path }.to_json

View file

@ -0,0 +1,12 @@
class CollectionsController < ApplicationController
def index
@user = User.find_by!(username: params[:username])
@collections = @user.collections.order(created_at: :desc)
end
def show
@collection = Collection.find(params[:id])
@user = @collection.user
@articles = @collection.articles.published.order(Arel.sql("COALESCE(crossposted_at, published_at) ASC"))
end
end

View file

@ -193,6 +193,13 @@ module ApplicationHelper
"#{start_year} - #{current_year}"
end
def collection_link(collection, **kwargs)
size_string = "#{collection.articles.published.size} Part Series"
body = collection.slug.present? ? "#{collection.slug} (#{size_string})" : size_string
link_to body, collection.path, **kwargs
end
def email_link(type = :default, text: nil, additional_info: nil)
# The allowed types for type is :default, :business, :privacy, and members.
# These options can be found in field :email_addresses of models/site_config.rb

View file

@ -17,14 +17,6 @@ module ArticlesHelper
article.comments_blob.include?("youtube")
end
def collection_link_class(current_article, linked_article)
if current_article.id == linked_article.id
"current-article"
elsif !linked_article.published
"coming-soon"
end
end
def image_tag_or_inline_svg_tag(service_name, width: nil, height: nil)
name = "#{service_name}-logo.svg"

View file

@ -12,6 +12,10 @@ class Collection < ApplicationRecord
Collection.find_or_create_by(slug: slug, user: user)
end
def path
"/#{user.username}/series/#{id}"
end
private
def touch_articles

View file

@ -3,11 +3,8 @@
<% if collection && collection_size > 1 %>
<div class="article-collection-wrapper article-collection-wrapper-<%= position %>">
<% if collection.slug.present? %>
<p><%= collection.slug %> (<%= collection_size %> Part Series)</p>
<% else %>
<p><%= collection_size %> Part Series</p>
<% end %>
<p><%= collection_link(collection) %></p>
<div class="article-collection">
<% articles.each_with_index do |article, i| %>
<% if collection_size > 5 && i == 2 %>
@ -24,7 +21,7 @@
<a
href="<%= article.path %>"
class="<%= collection_link_class(rendered_article, article) %> <%= "collection-link-hidden" if collection_size > 5 && (i > 1 && i < collection_size - 2) %>"
class="<%= "current-article" if rendered_article.id == article.id %> <%= "collection-link-hidden" if collection_size > 5 && (i > 1 && i < collection_size - 2) %>"
data-preload-image="<%= cloud_cover_url(article.main_image) %>"
title="Published <%= article.readable_publish_date %>">
<%= i + 1 %>) <%= article.title %>

View file

@ -0,0 +1,17 @@
<% title title_string %>
<link rel="canonical" href="<%= app_url("/about") %>" />
<meta name="description" content="<%= title_string %> on <%= community_name %>">
<meta name="keywords" content="<%= SiteConfig.meta_keywords[:default] %>">
<meta property="og:type" content="article" />
<meta property="og:url" content="<%= app_url("/about") %>" />
<meta property="og:title" content="About <%= community_name %>" />
<meta property="og:image" content="<%= SiteConfig.main_social_image %>" />
<meta property="og:description" content="<%= community_qualified_name %> is great!" />
<meta property="og:site_name" content="<%= community_qualified_name %>" />
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@<%= SiteConfig.social_media_handles["twitter"] %>">
<meta name="twitter:title" content="About <%= community_name %>">
<meta name="twitter:description" content="<%= community_qualified_name %> is great!">
<meta name="twitter:image:src" content="<%= SiteConfig.main_social_image %>">

View file

@ -0,0 +1,25 @@
<% title_string = "#{@user.name}'s Series" %>
<%= content_for :page_meta do %>
<%= render "collections/meta", title_string: title_string %>
<% end %>
<div class="crayons-layout crayons-layout--1-col">
<header class="px-2 m:px-0">
<h1><%= title_string %></h1>
</header>
<div class="grid gap-2 m:gap-4 l:gap-6 m:grid-cols-2 l:grid-cols-3 px-2 m:px-0">
<% if @collections.present? %>
<% @collections.each do |collection| %>
<div class="crayons-card branded-4 p-4 m:p-6 m:pt-4 flex flex-col relative">
<h3 class="mb-2">
<%= collection_link(collection, class: "crayons-link") %>
</h3>
</div>
<% end %>
<% else %>
<p>This user doesn't have any series</p>
<% end %>
</div>
</div>

View file

@ -0,0 +1,24 @@
<% title_string = "#{@collection.slug} Series' Articles" %>
<%= content_for :page_meta do %>
<%= render "collections/meta", title_string: title_string %>
<% end %>
<div class="crayons-layout crayons-layout--1-col">
<header class="px-2 m:px-0">
<h1><%= title_string %></h1>
<%= link_to "Back to #{@user.name}'s Series", user_series_path(@user.username) %>
</header>
<div class="articles-list" id="articles-list">
<div class="substories" id="substories">
<% if @articles.present? %>
<% @articles.each do |article| %>
<%= render "articles/single_story", story: article.decorate, featured: article.main_image.present? %>
<% end %>
<% else %>
<p>This series doesn't have any articles</p>
<% end %>
</div>
</div>
</div>

View file

@ -457,6 +457,9 @@ Rails.application.routes.draw do
get "/:timeframe" => "stories#index", :constraints => { timeframe: /latest/ }
get "/:username/series" => "collections#index", :as => "user_series"
get "/:username/series/:id" => "collections#show"
# Legacy comment format (might still be floating around app, and external links)
get "/:username/:slug/comments" => "comments#index"
get "/:username/:slug/comments/:id_code" => "comments#index"

View file

@ -108,6 +108,22 @@ RSpec.describe ApplicationHelper, type: :helper do
end
end
describe "#collection_link" do
let(:collection) { create(:collection, :with_articles) }
it "returns an 'a' tag" do
expect(helper.collection_link(collection)).to have_selector("a")
end
it "sets the correct href" do
expect(helper.collection_link(collection)).to have_link(href: collection.path)
end
it "has the correct text in the a tag" do
expect(helper.collection_link(collection)).to have_text("#{collection.slug} (#{collection.articles.published.size} Part Series)")
end
end
describe "#email_link" do
before do
allow(SiteConfig).to receive(:email_addresses).and_return(

View file

@ -34,6 +34,12 @@ RSpec.describe Collection, type: :model do
end
end
describe "path" do
it "returns the correct path" do
expect(collection.path).to eq("/#{collection.user.username}/series/#{collection.id}")
end
end
context "when callbacks are triggered after touch" do
it "touches all articles in the collection" do
before_times = collection.articles.order(updated_at: :desc).pluck(:updated_at).map(&:to_i)

View file

@ -0,0 +1,20 @@
require "rails_helper"
RSpec.describe "Collections", type: :request do
let(:user) { create(:user) }
let(:collection) { create(:collection, :with_articles, user: user) }
describe "GET user collections index" do
it "returns 200" do
get "/#{user.username}/series"
expect(response).to have_http_status(:ok)
end
end
describe "GET user collection show" do
it "returns 200" do
get collection.path
expect(response).to have_http_status(:ok)
end
end
end

View file

@ -0,0 +1,17 @@
require "rails_helper"
RSpec.describe "Viewing a collection", type: :system do
let(:user) { create(:user) }
let(:collection) { create(:collection, :with_articles, user: user) }
before do
sign_in user
visit collection.path
end
it "shows all published articles" do
collection.articles.published.each do |article|
expect(page.body).to have_link(article.title)
end
end
end

View file

@ -0,0 +1,18 @@
require "rails_helper"
RSpec.describe "Visiting collections", type: :system do
let(:user) { create(:user) }
let!(:collection1) { create(:collection, :with_articles, user: user) }
let!(:collection2) { create(:collection, user: user) }
before do
sign_in user
visit user_series_path(user.username)
end
it "shows all collections", :aggregate_failures do
[collection1, collection2].each do |collection|
expect(page.body).to have_link("#{collection.slug} (#{collection.articles.published.size} Part Series)")
end
end
end