Skip to content

Commit 09efa79

Browse files
committed
Turn /feeds into LiveView
This migration only turns the top-level template into a LiveView. It does not change anything rendered on the page. The page continues to be rendered as an Elm app on the client. This is done to keep this commit as small as possible.
1 parent 6b361de commit 09efa79

File tree

13 files changed

+150
-2
lines changed

13 files changed

+150
-2
lines changed

config/config.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ config :ex_rss,
1212
# Configures the endpoint
1313
config :ex_rss, ExRssWeb.Endpoint,
1414
url: [host: "localhost"],
15+
# TODO
16+
# `secret_key_base` seems to have been removed by Phoenix 1.7 or an earlier
17+
# version.
1518
secret_key_base: System.get_env("SECRET_KEY_BASE"),
19+
live_view: [signing_salt: "abcdefgh"],
1620
render_errors: [view: ExRssWeb.ErrorView, accepts: ~w(html json)],
1721
pubsub_server: ExRss.PubSub
1822

config/dev.exs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import Config
88
# with esbuild to recompile .js and .css sources.
99
config :ex_rss, ExRssWeb.Endpoint,
1010
http: [port: 4000, protocol_options: [max_header_value_length: 8192]],
11+
# TODO
12+
# `secret_key_base` seems to have been removed by Phoenix 1.7 or an earlier
13+
# version.
1114
secret_key_base: String.duplicate("abcdefgh", 8),
1215
debug_errors: true,
1316
code_reloader: true,
@@ -41,6 +44,12 @@ config :ex_rss, ExRssWeb.Endpoint,
4144
# in production as building large stacktraces may be expensive.
4245
config :phoenix, :stacktrace_depth, 20
4346

47+
config :phoenix_live_view,
48+
# Include HEEx debug annotations as HTML comments in rendered markup
49+
debug_heex_annotations: true,
50+
# Enable helpful, but potentially expensive runtime checks
51+
enable_expensive_runtime_checks: true
52+
4453
# Configure your database
4554
config :ex_rss, ExRss.Repo,
4655
adapter: Ecto.Adapters.Postgres,

lib/ex_rss_web.ex

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ defmodule ExRssWeb do
4343
end
4444
end
4545

46+
# 2024-12-03
47+
# `view` was generated before Phoenix 1.7 and would not be generated by
48+
# Phoenix 1.7. It is kept around to keep existing code working and to keep
49+
# the initial migration to LiveView small.
4650
def view do
4751
quote do
4852
use Phoenix.View, root: "lib/ex_rss_web/templates", namespace: ExRssWeb
@@ -62,9 +66,35 @@ defmodule ExRssWeb do
6266
end
6367
end
6468

69+
def live_view do
70+
quote do
71+
use Phoenix.LiveView,
72+
layout: {ExRssWeb.Layouts, :app}
73+
74+
unquote(html_helpers())
75+
end
76+
end
77+
78+
def html do
79+
quote do
80+
use Phoenix.Component
81+
82+
# Import convenience functions from controllers
83+
import Phoenix.Controller,
84+
only: [get_csrf_token: 0, view_module: 1, view_template: 1]
85+
86+
# Include general helpers for rendering HTML
87+
unquote(html_helpers())
88+
end
89+
end
90+
6591
def router do
6692
quote do
6793
use Phoenix.Router
94+
95+
import Plug.Conn
96+
import Phoenix.Controller
97+
import Phoenix.LiveView.Router
6898
end
6999
end
70100

@@ -79,6 +109,24 @@ defmodule ExRssWeb do
79109
end
80110
end
81111

112+
defp html_helpers do
113+
quote do
114+
# HTML escaping functionality
115+
import Phoenix.HTML
116+
# TODO
117+
# Phoenix 1.7 would also generate `import ExRssWeb.CoreComponents`. I did
118+
# not add this import to keep the initial migration to `LiveView` small.
119+
# Translation
120+
import ExRssWeb.Gettext
121+
122+
# Shortcut for generating JS commands
123+
alias Phoenix.LiveView.JS
124+
125+
# Routes generation with the ~p sigil
126+
unquote(verified_routes())
127+
end
128+
end
129+
82130
def verified_routes do
83131
quote do
84132
use Phoenix.VerifiedRoutes,

lib/ex_rss_web/components/layouts.ex

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
defmodule ExRssWeb.Layouts do
2+
@moduledoc """
3+
This module holds different layouts used by your application.
4+
5+
See the `layouts` directory for all templates available.
6+
The "root" layout is a skeleton rendered as part of the
7+
application router. The "app" layout is set as the default
8+
layout on both `use ExRssWeb, :controller` and
9+
`use ExRssWeb, :live_view`.
10+
"""
11+
use ExRssWeb, :html
12+
13+
embed_templates "layouts/*"
14+
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%= @inner_content %>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<meta name="description" content="">
8+
<meta name="author" content="">
9+
10+
<title>Hello ExRss!</title>
11+
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"}>
12+
<script>window.userToken = "<%= assigns[:user_token] %>";</script>
13+
<script phx-track-static src={~p"/assets/app.js"}></script>
14+
</head>
15+
16+
<body class="m-6 grid grid-cols-12">
17+
<div class="col-span-12 md:col-start-3 md:col-span-8">
18+
<p role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
19+
<p role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
20+
21+
<%= @inner_content %>
22+
23+
</div> <!-- /container -->
24+
</body>
25+
</html>

lib/ex_rss_web/feed_live/index.ex

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
defmodule ExRssWeb.FeedLive.Index do
2+
use ExRssWeb, :live_view
3+
4+
def mount(_params, %{"api_token" => api_token} = _session, socket) do
5+
{:ok, assign(socket, :api_token, api_token)}
6+
end
7+
8+
# 2024-12-03
9+
# This `use` is needed for `content_tag` to be available in `elm_module`.
10+
use Phoenix.HTML
11+
12+
def elm_module(module, params \\ %{}, attrs \\ []) do
13+
{tag, attrs} = Keyword.pop(attrs, :tag, :div)
14+
15+
data_attributes = [
16+
"data-elm-module": module,
17+
"data-elm-params": html_escape(Jason.encode!(params))
18+
]
19+
20+
content_tag(tag, "", Keyword.merge(attrs, data_attributes))
21+
end
22+
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<%=
2+
elm_module("App.Feeds", %{apiToken: @api_token})
3+
%>

lib/ex_rss_web/plugs/assign_api_token.ex

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@ defmodule ExRssWeb.Plug.AssignApiToken do
1414
if %User{id: id} = conn.assigns[:current_user] do
1515
token = Phoenix.Token.sign(@context, salt, %Account{id: id})
1616

17-
assign(conn, :api_token, token)
17+
conn
18+
# 2024-12-03
19+
# `assign` is used to assign to `conn.assigns` in order to have the token
20+
# available in regular templates.
21+
|> assign(:api_token, token)
22+
# `put_session` is used to have the token available in LiveViews’
23+
# `mount`.
24+
|> put_session(:api_token, token)
1825
else
1926
conn
2027
end

lib/ex_rss_web/router.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule ExRssWeb.Router do
77
plug :accepts, ["html"]
88
plug :fetch_session
99
plug :fetch_flash
10+
plug :put_root_layout, {ExRssWeb.Layouts, :root}
1011
plug :protect_from_forgery
1112
plug :put_secure_browser_headers
1213

@@ -44,7 +45,7 @@ defmodule ExRssWeb.Router do
4445
pipe_through :authenticated
4546

4647
scope "/feeds" do
47-
resources "/", FeedController, only: [:index]
48+
live "/", FeedLive.Index, :index
4849

4950
get "/new", FeedController, :new
5051
end

lib/ex_rss_web/views/layout_view.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
# 2024-12-03
2+
# `LayoutView` was generated before Phoenix 1.7. Deleting this module when
3+
# migrating to Phoenix 1.7 patterns resulted in errors similar to the following
4+
# one in tests:
5+
#
6+
# ```
7+
# ** (ArgumentError) no "app" html template defined for ExRssWeb.LayoutView
8+
# (the module does not exist)
9+
# ```
10+
#
11+
# It is kept around to keep existing code working and to keep the initial
12+
# migration to LiveView small.
113
defmodule ExRssWeb.LayoutView do
214
use ExRssWeb, :view
315
end

mix.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ defmodule ExRss.Mixfile do
4242
{:postgrex, "~> 0.19.1"},
4343
{:phoenix_html, "~> 3.0"},
4444
{:phoenix_live_reload, "~> 1.0", only: :dev},
45+
{:phoenix_live_view, "~> 0.20"},
4546
{:gettext, "~> 0.11"},
4647
{:floki, "~> 0.24"},
4748
{:feeder_ex, "~> 1.1"},

mix.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.1", "96798325fab2fed5a824ca204e877b81f9afd2e480f581e81f7b4b64a5a477f2", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.17", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "0ae544ff99f3c482b0807c5cec2c8289e810ecacabc04959d82c3337f4703391"},
3535
"phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"},
3636
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"},
37+
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.17", "f396bbdaf4ba227b82251eb75ac0afa6b3da5e509bc0d030206374237dfc9450", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a61d741ffb78c85fdbca0de084da6a48f8ceb5261a79165b5a0b59e5f65ce98b"},
3738
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
3839
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
3940
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},

0 commit comments

Comments
 (0)