[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:
parent
8b882dd512
commit
115adbb1bd
17 changed files with 174 additions and 23 deletions
|
|
@ -319,10 +319,6 @@ article {
|
|||
color: var(--body-color-inverted);
|
||||
}
|
||||
|
||||
&.coming-soon {
|
||||
pointer-events: none;
|
||||
color: lighten($black, 78%);
|
||||
}
|
||||
&.collection-link-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -568,10 +568,6 @@
|
|||
color: var(--body-color-inverted);
|
||||
}
|
||||
|
||||
&.coming-soon {
|
||||
pointer-events: none;
|
||||
color: var(--base-80);
|
||||
}
|
||||
&.collection-link-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
12
app/controllers/collections_controller.rb
Normal file
12
app/controllers/collections_controller.rb
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 %>
|
||||
|
|
|
|||
17
app/views/collections/_meta.html.erb
Normal file
17
app/views/collections/_meta.html.erb
Normal 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 %>">
|
||||
25
app/views/collections/index.html.erb
Normal file
25
app/views/collections/index.html.erb
Normal 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>
|
||||
24
app/views/collections/show.html.erb
Normal file
24
app/views/collections/show.html.erb
Normal 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>
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
20
spec/requests/collections_spec.rb
Normal file
20
spec/requests/collections_spec.rb
Normal 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
|
||||
|
|
@ -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
|
||||
18
spec/system/collections/user_views_collections_spec.rb
Normal file
18
spec/system/collections/user_views_collections_spec.rb
Normal 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
|
||||
Loading…
Add table
Reference in a new issue