Skip to content

Commit d9fa3b0

Browse files
committed
Render feeds’ entries using LiveView
1 parent 515ce96 commit d9fa3b0

File tree

3 files changed

+102
-25
lines changed

3 files changed

+102
-25
lines changed

.formatter.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[
22
import_deps: [:ecto, :ecto_sql, :phoenix],
33
subdirectories: ["priv/*/migrations"],
4+
plugins: [Phoenix.LiveView.HTMLFormatter],
45
inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"]
56
]

lib/ex_rss_web/feed_live/index.ex

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
defmodule ExRssWeb.FeedLive.Index do
2+
import Ecto.Query
3+
24
use ExRssWeb, :live_view
35

46
alias ExRss.{Entry, Feed, Repo, User}
@@ -10,11 +12,22 @@ defmodule ExRssWeb.FeedLive.Index do
1012
) do
1113
oldest_unread_entry = User.oldest_unread_entry(current_user.id)
1214

15+
current_user = Repo.get!(User, current_user.id)
16+
17+
feeds =
18+
current_user
19+
|> Ecto.assoc(:feeds)
20+
|> Repo.all()
21+
|> Repo.preload(
22+
entries: from(e in Entry, where: e.read == false, order_by: [desc: e.posted_at])
23+
)
24+
1325
socket =
1426
socket
1527
|> assign(:current_user, current_user)
1628
|> assign(:api_token, api_token)
1729
|> assign(:oldest_unread_entry, oldest_unread_entry)
30+
|> stream(:feeds, feeds)
1831

1932
{:ok, socket}
2033
end
@@ -28,7 +41,11 @@ defmodule ExRssWeb.FeedLive.Index do
2841

2942
case Repo.update(changeset) do
3043
{:ok, entry} ->
31-
updated_feed = Repo.get!(Feed, entry.feed_id)
44+
updated_feed =
45+
Repo.get!(Feed, entry.feed_id)
46+
|> Repo.preload(
47+
entries: from(e in Entry, where: e.read == false, order_by: [desc: e.posted_at])
48+
)
3249

3350
update_broadcaster =
3451
Application.get_env(:ex_rss, :update_broadcaster, ExRss.Crawler.UpdateBroadcaster)
@@ -43,7 +60,12 @@ defmodule ExRssWeb.FeedLive.Index do
4360
oldest_unread_entry =
4461
User.oldest_unread_entry(socket.assigns.current_user.id)
4562

46-
{:noreply, assign(socket, :oldest_unread_entry, oldest_unread_entry)}
63+
socket =
64+
socket
65+
|> assign(:oldest_unread_entry, oldest_unread_entry)
66+
|> stream_insert(:feeds, updated_feed)
67+
68+
{:noreply, socket}
4769

4870
_ ->
4971
{:noreply, socket}
@@ -65,4 +87,66 @@ defmodule ExRssWeb.FeedLive.Index do
6587

6688
content_tag(tag, "", Keyword.merge(attrs, data_attributes))
6789
end
90+
91+
attr :entry, Entry, required: true
92+
93+
def entry(assigns) do
94+
~H"""
95+
<ul class="flex flex-col">
96+
<li class="flex flex-col md:flex-row">
97+
<div class="flex flex-col">
98+
<a href={@entry.url} target="_blank"><%= @entry.title %></a>
99+
<span><%= @entry.posted_at %></span>
100+
</div>
101+
102+
<div class="md:shrink-0 flex self-start mt-1 ml-auto space-x-4">
103+
<a
104+
href={@entry.url}
105+
target="_blank"
106+
aria-label={"View entry #{@entry.title}"}
107+
phx-click="mark_as_read"
108+
phx-value-entry-id={@entry.id}
109+
>
110+
<.icon name="hero-arrow-top-right-on-square-solid" />
111+
</a>
112+
<button aria-label="Mark as read" phx-click="mark_as_read" phx-value-entry-id={@entry.id}>
113+
<.icon name="hero-check-circle-solid" />
114+
</button>
115+
</div>
116+
</li>
117+
</ul>
118+
"""
119+
end
120+
121+
attr :entries, :list, required: true
122+
123+
def entries(assigns) do
124+
if length(assigns.entries) > 5 do
125+
assigns =
126+
assigns
127+
|> assign(:head_entries, Enum.take(assigns.entries, 2))
128+
|> assign(:number_of_entries_not_shown, length(assigns.entries) - 4)
129+
|> assign(:tail_entries, Enum.take(assigns.entries, -2))
130+
131+
~H"""
132+
<ul class="mb-6 flex flex-col space-y-4">
133+
<li :for={entry <- @head_entries}>
134+
<.entry entry={entry} />
135+
</li>
136+
<div><%= @number_of_entries_not_shown %> entries not shown</div>
137+
<li :for={entry <- @tail_entries}>
138+
<.entry entry={entry} />
139+
</li>
140+
</ul>
141+
"""
142+
else
143+
~H"""
144+
<ul class="mb-6 flex flex-col space-y-4">
145+
<li :for={entry <- @entries}>
146+
<.entry entry={entry} />
147+
</li>
148+
</ul>
149+
"""
150+
end
151+
end
68152
end
Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,21 @@
11
<h1 class="mb-2 font-bold">Oldest unread entry</h1>
22

3-
<%= if @oldest_unread_entry do %>
4-
<ul class="mb-6 flex flex-col">
5-
<li class="flex flex-col md:flex-row mt-4">
6-
<div class="flex flex-col">
7-
<a href={@oldest_unread_entry.url} target="_blank"><%= @oldest_unread_entry.title %></a>
8-
<span><%= @oldest_unread_entry.posted_at %></span>
9-
</div>
3+
<div class="mb-6">
4+
<%= if @oldest_unread_entry do %>
5+
<.entry entry={@oldest_unread_entry} />
6+
<% else %>
7+
<div>No entry found</div>
8+
<% end %>
9+
</div>
10+
11+
<ul id="live-feeds" phx-update="stream">
12+
<li :for={{dom_id, feed} <- @streams.feeds} id={dom_id}>
13+
<h1 class="mb-2 font-bold"><%= feed.title %></h1>
1014

11-
<div class="md:shrink-0 flex self-start mt-1 ml-auto space-x-4">
12-
<a href={@oldest_unread_entry.url} target="_blank" aria-label={"View entry #{@oldest_unread_entry.title}"} phx-click="mark_as_read" phx-value-entry-id={@oldest_unread_entry.id}>
13-
<.icon name="hero-arrow-top-right-on-square-solid" />
14-
</a>
15-
<button aria-label="Mark as read" phx-click="mark_as_read" phx-value-entry-id={@oldest_unread_entry.id}>
16-
<.icon name="hero-check-circle-solid" />
17-
</button>
18-
</div>
19-
</li>
20-
</ul>
21-
<% else %>
22-
<div class="mb-6">No entry found</div>
23-
<% end %>
15+
<.entries entries={feed.entries} />
16+
</li>
17+
</ul>
2418

2519
<div id="elm-app-feeds" phx-update="ignore">
26-
<%=
27-
elm_module("App.Feeds", %{apiToken: @api_token}, id: "elm-module-app-feeds")
28-
%>
20+
<%= elm_module("App.Feeds", %{apiToken: @api_token}, id: "elm-module-app-feeds") %>
2921
</div>

0 commit comments

Comments
 (0)