Skip to content

Commit

Permalink
Forward flashes and automatically pass via props (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
derrickreimer committed Jun 12, 2024
1 parent 4c11e9a commit c0af9b6
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- Support for propagating errors via an `assign_errors` helper ([#10](https://github.com/svycal/inertia-phoenix/issues/10))
- Preservation of assigned errors across redirects ([#10](https://github.com/svycal/inertia-phoenix/issues/10))
- Setup external redirects properly for Inertia requests ([#11](https://github.com/svycal/inertia-phoenix/issues/11))
- Forward flash contents across forced refreshes ([#13](https://github.com/svycal/inertia-phoenix/issues/13))
- Automatically pass Phoenix flash data via the `flash` prop

## 0.4.0

Expand Down
6 changes: 6 additions & 0 deletions lib/inertia/controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ defmodule Inertia.Controller do
shared
|> Map.merge(props)
|> resolve_props(only: only, except: except)
|> maybe_put_flash(conn)

conn
|> put_private(:inertia_page, %{component: component, props: props})
Expand Down Expand Up @@ -220,6 +221,11 @@ defmodule Inertia.Controller do
end
end

# Skip putting flash in the props if there's already `:flash` key assigned.
# Otherwise, put the flash in the props.
defp maybe_put_flash(%{flash: _} = props, _conn), do: props
defp maybe_put_flash(props, conn), do: Map.put(props, :flash, conn.assigns.flash)

defp send_response(%{private: %{inertia_request: true}} = conn) do
conn
|> put_status(200)
Expand Down
21 changes: 21 additions & 0 deletions lib/inertia/plug.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defmodule Inertia.Plug do
|> assign(:inertia_head, [])
|> put_private(:inertia_version, compute_version())
|> put_private(:inertia_error_bag, get_error_bag(conn))
|> merge_forwarded_flash()
|> fetch_inertia_errors()
|> detect_inertia()
end
Expand Down Expand Up @@ -152,10 +153,30 @@ defmodule Inertia.Plug do
conn
|> put_resp_header("x-inertia-location", request_url(conn))
|> put_resp_content_type("text/html")
|> forward_flash()
|> send_resp(:conflict, "")
|> halt()
end

defp forward_flash(%{assigns: %{flash: flash}} = conn)
when is_map(flash) and map_size(flash) > 0 do
put_session(conn, "inertia_flash", flash)
end

defp forward_flash(conn), do: conn

defp merge_forwarded_flash(conn) do
case get_session(conn, "inertia_flash") do
nil ->
conn

flash ->
conn
|> delete_session("inertia_flash")
|> assign(:flash, Map.merge(conn.assigns.flash, flash))
end
end

defp static_paths do
Application.get_env(:inertia, :static_paths, [])
end
Expand Down
85 changes: 74 additions & 11 deletions test/inertia_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ defmodule InertiaTest do

assert %{
"component" => "Home",
"props" => %{"text" => "Hello World", "errors" => %{}},
"props" => %{"text" => "Hello World", "errors" => %{}, "flash" => %{}},
"url" => "/",
"version" => @current_version
} = json_response(conn, 200)
Expand All @@ -38,7 +38,12 @@ defmodule InertiaTest do

assert %{
"component" => "Home",
"props" => %{"text" => "Hello World", "foo" => "bar", "errors" => %{}},
"props" => %{
"text" => "Hello World",
"foo" => "bar",
"errors" => %{},
"flash" => %{}
},
"url" => "/shared",
"version" => @current_version
} = json_response(conn, 200)
Expand Down Expand Up @@ -178,7 +183,8 @@ defmodule InertiaTest do
"lazy_1" => "lazy_1",
"lazy_3" => "lazy_3",
"nested" => %{"lazy_2" => "lazy_2"},
"errors" => %{}
"errors" => %{},
"flash" => %{}
},
"url" => "/lazy",
"version" => @current_version
Expand All @@ -196,7 +202,11 @@ defmodule InertiaTest do

assert json_response(conn, 200) == %{
"component" => "Home",
"props" => %{"a" => %{"b" => %{"c" => "c", "e" => %{"f" => "f"}}}, "errors" => %{}},
"props" => %{
"a" => %{"b" => %{"c" => "c", "e" => %{"f" => "f"}}},
"errors" => %{},
"flash" => %{}
},
"url" => "/nested",
"version" => @current_version
}
Expand All @@ -213,7 +223,7 @@ defmodule InertiaTest do

assert json_response(conn, 200) == %{
"component" => "Home",
"props" => %{"a" => %{"b" => %{"c" => "c"}}, "errors" => %{}},
"props" => %{"a" => %{"b" => %{"c" => "c"}}, "errors" => %{}, "flash" => %{}},
"url" => "/nested",
"version" => @current_version
}
Expand All @@ -230,7 +240,7 @@ defmodule InertiaTest do

assert json_response(conn, 200) == %{
"component" => "Home",
"props" => %{"a" => "a", "important" => "stuff", "errors" => %{}},
"props" => %{"a" => "a", "important" => "stuff", "errors" => %{}, "flash" => %{}},
"url" => "/always",
"version" => @current_version
}
Expand All @@ -249,7 +259,8 @@ defmodule InertiaTest do
"component" => "Home",
"props" => %{
"a" => %{"b" => %{"c" => "c", "e" => %{"f" => "f", "g" => "g"}, "d" => "d"}},
"errors" => %{}
"errors" => %{},
"flash" => %{}
},
"url" => "/nested",
"version" => @current_version
Expand All @@ -265,7 +276,7 @@ defmodule InertiaTest do

assert json_response(conn, 200) == %{
"component" => "Home",
"props" => %{"b" => "b", "errors" => %{}},
"props" => %{"b" => "b", "errors" => %{}, "flash" => %{}},
"url" => "/tagged_lazy",
"version" => @current_version
}
Expand All @@ -282,7 +293,7 @@ defmodule InertiaTest do

assert json_response(conn, 200) == %{
"component" => "Home",
"props" => %{"a" => "a", "errors" => %{}},
"props" => %{"a" => "a", "errors" => %{}, "flash" => %{}},
"url" => "/tagged_lazy",
"version" => @current_version
}
Expand All @@ -298,7 +309,8 @@ defmodule InertiaTest do
assert json_response(conn, 200) == %{
"component" => "Home",
"props" => %{
"errors" => %{"settings.theme" => "can't be blank", "name" => "can't be blank"}
"errors" => %{"settings.theme" => "can't be blank", "name" => "can't be blank"},
"flash" => %{}
},
"url" => "/changeset_errors",
"version" => @current_version
Expand All @@ -321,7 +333,8 @@ defmodule InertiaTest do
"settings.theme" => "can't be blank",
"name" => "can't be blank"
}
}
},
"flash" => %{}
},
"url" => "/changeset_errors",
"version" => @current_version
Expand Down Expand Up @@ -365,6 +378,56 @@ defmodule InertiaTest do
assert get_resp_header(conn, "x-inertia-location") == ["http://www.example.com/"]
end

test "automatically includes flash in props", %{conn: conn} do
conn =
conn
|> patch(~p"/")

assert html_response(conn, 302)

conn =
conn
|> recycle()
|> get("/")

assert html_response(conn, 200) =~ ~s("flash":{"info":"Patched") |> html_escape()
end

test "does not clobber the flash prop if manually set", %{conn: conn} do
conn =
conn
|> get(~p"/overridden_flash")

assert html_response(conn, 200) =~ ~s("flash":{"foo":"bar") |> html_escape()
end

test "forwards flash across forced refreshes", %{conn: conn} do
conn =
conn
|> patch(~p"/")

assert html_response(conn, 302)

# The next redirect hop triggers a forced refresh...
conn =
conn
|> recycle()
|> put_req_header("x-inertia", "true")
|> put_req_header("x-inertia-version", "different")
|> get("/")

assert html_response(conn, 409)
assert get_resp_header(conn, "x-inertia-location") == ["http://www.example.com/"]

# After the hop, flash should be present in the props
conn =
conn
|> recycle()
|> get("/")

assert html_response(conn, 200) =~ ~s("flash":{"info":"Patched") |> html_escape()
end

defp html_escape(content) do
content
|> Phoenix.HTML.html_escape()
Expand Down
10 changes: 10 additions & 0 deletions test/support/my_app/lib/my_app_web/controllers/page_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,28 @@ defmodule MyAppWeb.PageController do
redirect(conn, external: "http://www.example.com/")
end

def overridden_flash(conn, _params) do
conn
|> assign(:page_title, "Home")
|> assign_prop(:flash, %{foo: "bar"})
|> render_inertia("Home")
end

def update(conn, _params) do
conn
|> put_flash(:info, "Updated")
|> redirect(to: "/")
end

def patch(conn, _params) do
conn
|> put_flash(:info, "Patched")
|> redirect(to: "/")
end

def delete(conn, _params) do
conn
|> put_flash(:info, "Deleted")
|> redirect(to: "/")
end

Expand Down
1 change: 1 addition & 0 deletions test/support/my_app/lib/my_app_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule MyAppWeb.Router do
get "/redirect_on_error", PageController, :redirect_on_error
get "/bad_error_map", PageController, :bad_error_map
get "/external_redirect", PageController, :external_redirect
get "/overridden_flash", PageController, :overridden_flash
put "/", PageController, :update
patch "/", PageController, :patch
delete "/", PageController, :delete
Expand Down

0 comments on commit c0af9b6

Please sign in to comment.