Skip to content

Commit

Permalink
Simplify rendering of posts index
Browse files Browse the repository at this point in the history
  • Loading branch information
jerodsanto committed Jan 2, 2024
1 parent 42f6cae commit d378172
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 232 deletions.
2 changes: 2 additions & 0 deletions assets/app/components/news_item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@

.news_item--sponsor & { top: 45px; }

.page_news_item & { left: -50px; }

&-icon,
img {
width: $size;
Expand Down
4 changes: 2 additions & 2 deletions lib/changelog/schema/factory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,11 @@ defmodule Changelog.Factory do
end

def published_post_factory do
%Changelog.Post{post_factory() | published: true, published_at: hours_ago(1)}
%Changelog.Post{publishable_post_factory() | published: true, published_at: hours_ago(1)}
end

def scheduled_post_factory do
%Changelog.Post{post_factory() | published: true, published_at: hours_from_now(1)}
%Changelog.Post{publishable_post_factory() | published: true, published_at: hours_from_now(1)}
end

def sponsor_factory do
Expand Down
219 changes: 0 additions & 219 deletions lib/changelog/schema/news/news_item.ex
Original file line number Diff line number Diff line change
Expand Up @@ -194,225 +194,6 @@ defmodule Changelog.NewsItem do
|> file_changeset(attrs)
end

def get_pinned_non_feed_news_items do
from(news_item in __MODULE__,
left_join: author in assoc(news_item, :author),
left_join: comments in assoc(news_item, :comments),
left_join: submitter in assoc(news_item, :submitter),
left_join: source in assoc(news_item, :source),
left_join: logger in assoc(news_item, :logger),
left_join: news_item_topics in assoc(news_item, :news_item_topics),
left_join: news_item_topics_topic in assoc(news_item_topics, :topic),
where: news_item.status == ^:published,
where: news_item.published_at <= ^Timex.now(),
where: not news_item.feed_only,
where: news_item.pinned,
order_by: [desc: :published_at, asc: news_item_topics.position],
preload: [
author: author,
comments: comments,
submitter: submitter,
topics: news_item_topics_topic,
source: source,
logger: logger
]
)
|> Repo.all()
end

def get_unpinned_non_feed_news_items(params) do
page =
from(news_item in __MODULE__,
where: news_item.status == ^:published,
where: news_item.published_at <= ^Timex.now(),
where: not news_item.feed_only,
where: not news_item.pinned,
order_by: [desc: :published_at]
)
|> Repo.paginate(Map.put(params, :page_size, 20))

news_item_ids =
page
|> Map.get(:entries)
|> Enum.map(fn news_item ->
news_item.id
end)

results =
from(news_item in __MODULE__,
left_join: author in assoc(news_item, :author),
left_join: comments in assoc(news_item, :comments),
left_join: submitter in assoc(news_item, :submitter),
left_join: source in assoc(news_item, :source),
left_join: logger in assoc(news_item, :logger),
left_join: news_item_topics in assoc(news_item, :news_item_topics),
left_join: news_item_topics_topic in assoc(news_item_topics, :topic),
where: news_item.id in ^news_item_ids,
order_by: [desc: :published_at, asc: news_item_topics.position],
preload: [
author: author,
comments: comments,
submitter: submitter,
topics: news_item_topics_topic,
source: source,
logger: logger
]
)
|> Repo.all()

{page, results}
end

def get_post_news_items(params) do
page =
from(news_item in __MODULE__,
where: like(news_item.object_id, ^"posts:%"),
where: news_item.status == ^:published,
where: news_item.published_at <= ^Timex.now(),
where: not news_item.feed_only,
order_by: [desc: :inserted_at]
)
|> Repo.paginate(Map.put(params, :page_size, 15))

news_item_ids =
page
|> Map.get(:entries)
|> Enum.map(fn news_item ->
news_item.id
end)

results =
from(news_item in __MODULE__,
left_join: author in assoc(news_item, :author),
left_join: logger in assoc(news_item, :logger),
left_join: submitter in assoc(news_item, :submitter),
left_join: topics in assoc(news_item, :topics),
left_join: source in assoc(news_item, :source),
left_join: comments in assoc(news_item, :comments),
left_join: news_item_topics in assoc(news_item, :news_item_topics),
left_join: news_item_topics_topic in assoc(news_item_topics, :topic),
where: news_item.id in ^news_item_ids,
order_by: [desc: :inserted_at, asc: news_item_topics.position, desc: topics.id],
preload: [
author: author,
comments: comments,
logger: logger,
submitter: submitter,
source: source,
topics: topics,
news_item_topics: {news_item_topics, topic: news_item_topics_topic}
]
)
|> Repo.all()
|> batch_load_objects()

{page, results}
end

def batch_load_objects(news_items) do
{episodes, posts} =
Enum.split_with(news_items, fn news_item ->
news_item.type == :audio
end)

episode_ids =
episodes
|> Enum.map(fn
%{object_id: nil} ->
nil

%{object_id: object_id} ->
[_podcast_id, episode_id] = String.split(object_id, ":")
episode_id

_ ->
nil
end)
|> Enum.reject(fn
nil -> true
_ -> false
end)

# Only hit the DB if there are episodes to resolve
episode_data =
if episode_ids == [] do
[]
else
from(episode in Episode.exclude_transcript(),
left_join: podcast in assoc(episode, :podcast),
left_join: episode_guests in assoc(episode, :episode_guests),
left_join: person in assoc(episode_guests, :person),
left_join: guests in assoc(episode, :guests),
left_join: hosts in assoc(episode, :hosts),
where: episode.id in ^episode_ids,
order_by: [asc: episode_guests.position],
preload: [
podcast: podcast,
episode_guests: {episode_guests, person: person},
guests: guests,
hosts: hosts
]
)
|> Repo.all()
end

post_ids =
posts
|> Enum.map(fn
%{object_id: nil} ->
nil

%{object_id: object_id} ->
[_, slug] = String.split(object_id, ":")
slug

_ ->
nil
end)

# Only hit the DB if there are posts to resolve
post_data =
if post_ids == [] do
[]
else
from(post in Post.published(),
left_join: author in assoc(post, :author),
left_join: editor in assoc(post, :editor),
left_join: post_topics in assoc(post, :post_topics),
left_join: post_topics_topic in assoc(post_topics, :topic),
left_join: topics in assoc(post, :topics),
where: post.slug in ^post_ids,
order_by: [asc: post_topics.position, asc: post_topics_topic.id],
preload: [
author: author,
editor: editor,
post_topics: {post_topics, topic: post_topics_topic},
topics: topics
]
)
|> Repo.all()
end

news_items
|> Enum.map(fn
%{object_id: nil} = result ->
result

%{type: :audio, object_id: object_id} = result ->
[_podcast_id, episode_id] = String.split(object_id, ":")

object =
Enum.find(episode_data, fn episode -> Integer.to_string(episode.id) == episode_id end)

%{result | object: object}

result ->
[_, slug] = String.split(result.object_id, ":")
object = Enum.find(post_data, fn post -> post.slug == slug end)
%{result | object: object}
end)
end

def slug(item) do
item.headline
|> String.downcase()
Expand Down
7 changes: 5 additions & 2 deletions lib/changelog_web/controllers/post_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ defmodule ChangelogWeb.PostController do
alias Changelog.{Post, NewsItem, NewsItemComment}

def index(conn, params) do
{page, items} = NewsItem.get_post_news_items(params)
page =
Post.published()
|> Post.newest_first(:published_at)
|> Post.preload_all()
|> Repo.paginate(Map.put(params, :page_size, 15))

conn
|> assign(:page, page)
|> assign(:items, items)
|> render(:index)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/changelog_web/templates/page/index.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</nav>


<%= if Enum.any?(@page) do %>
<%= if Enum.any?(@page.entries) do %>
<%= render_many(@page, EpisodeView, "_item.html") %>

<nav class="load_more" aria-label="Pagination">
Expand Down
65 changes: 65 additions & 0 deletions lib/changelog_web/templates/post/_item.html.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<article class="news_item">
<header class="news_item-header">
<p class="news_item-source">
<%= link to: PersonView.profile_path(@post.author), title: @post.author.name, class: "news_item-source-image" do %>
<%= SharedHelpers.lazy_image(PersonView.avatar_url(@post.author), @post.author.name, width: 70, height: 70) %>
<% end %>

<%= link("changelog.com/posts", to: ~p"/posts", title: "Changelog Posts") %>
</p>

<h2 class="news_item-title"><%= link(@post.title, to: ~p"/posts/#{@post.slug}") %></h2>
</header>

<div class="news_item-toolbar">
<div class="news_item-toolbar-item news_item-toolbar-meta">
<span class="news_item-toolbar-meta-item">
by <strong><%= link(@post.author.name, to: PersonView.profile_path(@post.author)) %></strong>
</span>

<span class="news_item-toolbar-meta-item">
<%= TimeView.ts(@post.published_at, :date) %>
</span>

<%= if Enum.any?(@post.topics) do %>
<% {main_topics, additional_topics} = Enum.split(@post.topics, 1) %>

<span class="news_item-toolbar-meta-item">
<%= for topic <- main_topics do %>
<%= link("##{topic.slug}", to: ~p"/topic/#{topic.slug}", title: "View #{topic.name}") %>
<% end %>

<%= if Enum.any?(additional_topics) do %>
<span class="has-tooltip">+<%= length(additional_topics) %></span>
<div class="tooltip tooltip--topics">
<div class="tooltip-arrow"></div>
<div class="tooltip-wrap">
<ul class="tooltip-list">
<%= for topic <- additional_topics do %>
<li>
<%= link("##{topic.slug}", to: ~p"/topic/#{topic.slug}", title: "View #{topic.name}") %>
</li>
<% end %>
</ul>
</div>
</div>
<% end %>
</span>
<% end %>
</div>

</div>

<div class="news_item-content richtext">
<%= @post.tldr |> SharedHelpers.md_to_html() |> raw() %>
</div>

<footer class="news_item-footer">
<span class="news_item-footer-item">
<%= link("Read On", to: ~p"/posts/#{@post.slug}") %> &bull;
<%= link("Discuss", to: ~p"/posts/#{@post.slug}" <> "#discussion") %>
&bull; <%= link("Share", to: ~p"/posts/#{@post.slug}", title: "Copy URL for easy sharing", data: [copy: true]) %>
</span>
</footer>

</article>
7 changes: 4 additions & 3 deletions lib/changelog_web/templates/post/index.html.eex
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<section>
<a id="feed" class="anchor"></a>
<div class="feed">
<%= for item <- @items do %>
<%= render(NewsItemView, "_summary.html", Map.merge(assigns, %{item: item, style: "date"})) %>
<%= if Enum.any?(@page.entries) do %>
<%= render_many(@page, __MODULE__, "_item.html") %>

<%= render(SharedView, "_more_button.html", assigns) %>
<% end %>
<%= render(SharedView, "_more_button.html", assigns) %>
</div>
</section>
8 changes: 3 additions & 5 deletions test/changelog_web/controllers/post_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ defmodule ChangelogWeb.PostControllerTest do

test "getting the posts index", %{conn: conn} do
p1 = insert(:published_post)
i1 = insert(:published_news_item, object_id: "posts:#{p1.slug}")
p2 = insert(:published_post)
i2 = insert(:published_news_item, object_id: "posts:#{p2.slug}")
unpublished = insert(:post, published: false)
scheduled = insert(:scheduled_post)

conn = get(conn, Routes.post_path(conn, :index))
conn = get(conn, ~p"/posts")

assert conn.status == 200
assert conn.resp_body =~ i1.headline
assert conn.resp_body =~ i2.headline
assert conn.resp_body =~ p1.tldr
assert conn.resp_body =~ p2.tldr
refute conn.resp_body =~ unpublished.title
refute conn.resp_body =~ scheduled.title
end
Expand Down

0 comments on commit d378172

Please sign in to comment.