Skip to content

Commit

Permalink
Allow to patch (navigate patching the content) to another site (#621)
Browse files Browse the repository at this point in the history
  • Loading branch information
leandrocp committed Oct 29, 2024
1 parent 8050aa9 commit 7b04a89
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 19 deletions.
39 changes: 39 additions & 0 deletions lib/beacon/private.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule Beacon.Private do
@moduledoc false

# Concentrate calls to private APIs so it's easier to track breaking changes and document them,
# in case we need to make changes or understand why we had to call such APIs.

# Should be avoided as much as possible.

@doc """
On page navigation, the request might actually hit a different site than the one defined by the current session.
That's the case for a live patch to a full URL, for eg:
User is in https://sitea.com/page1 and clicks on a link to https://siteb.com/page2 through a `<.link patch={...}>` component,
and since LV allows such navigation, we must do it as well but we need fetch the session defined for the requested URL and update the state accordingly,
otherwise the page will either not be found or worse could render the wrong content.
Relative paths are not supported because there's no safe way to know if a `/relative` path belongs to site A or B, that's a LV contraint.
We use the private function `Phoenix.LiveView.Route.live_link_info/3` in order to keep the same behavior as LV.
"""
def site_from_session(endpoint, router, url, view) do
case Phoenix.LiveView.Route.live_link_info(endpoint, router, url) do
{_,
%{
view: ^view,
live_session: %{
extra: %{
session: %{"beacon_site" => site}
}
}
}} ->
site

_ ->
nil
end
end
end
4 changes: 4 additions & 0 deletions lib/beacon/pub_sub.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ defmodule Beacon.PubSub do
Phoenix.PubSub.subscribe(@pubsub, topic_page(site, path))
end

def unsubscribe_to_page(site, path) do
Phoenix.PubSub.unsubscribe(@pubsub, topic_page(site, path))
end

def page_loaded(%Content.Page{} = page) do
page.site
|> topic_page(page.path)
Expand Down
52 changes: 33 additions & 19 deletions lib/beacon/web/live/page_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -96,25 +96,39 @@ defmodule Beacon.Web.PageLive do
end
end

def handle_params(params, _url, socket) do
%{beacon: %{site: site}} = socket.assigns
%{"path" => path_info} = params
page = RouterServer.lookup_page!(site, path_info)
live_data = Beacon.Web.DataSource.live_data(site, path_info, Map.drop(params, ["path"]))
beacon_assigns = BeaconAssigns.new(site, page, live_data, path_info, params)

socket =
socket
|> Component.assign(live_data)
# TODO: remove deprecated @beacon_live_data
|> Component.assign(:beacon_live_data, live_data)
# TODO: remove deprecated @beacon_path_params
|> Component.assign(:beacon_path_params, beacon_assigns.path_params)
# TODO: remove deprecated @beacon_query_params
|> Component.assign(:beacon_query_params, beacon_assigns.query_params)
|> Component.assign(:beacon, beacon_assigns)

{:noreply, push_event(socket, "beacon:page-updated", %{meta_tags: Beacon.Web.DataSource.meta_tags(socket.assigns)})}
def handle_params(params, url, socket) do
case Beacon.Private.site_from_session(socket.endpoint, socket.router, url, __MODULE__) do
nil ->
raise Beacon.Web.NotFoundError, """
no page was found for url #{url}
Make sure a page was created for that url.
"""

site ->
%{"path" => path_info} = params
page = RouterServer.lookup_page!(site, path_info)
live_data = Beacon.Web.DataSource.live_data(site, path_info, Map.drop(params, ["path"]))
beacon_assigns = BeaconAssigns.new(site, page, live_data, path_info, params)

if socket.assigns.beacon.site != site do
Beacon.PubSub.unsubscribe_to_page(socket.assigns.beacon.site, path_info)
Beacon.PubSub.subscribe_to_page(site, path_info)
end

socket =
socket
|> Component.assign(live_data)
# TODO: remove deprecated @beacon_live_data
|> Component.assign(:beacon_live_data, live_data)
# TODO: remove deprecated @beacon_path_params
|> Component.assign(:beacon_path_params, beacon_assigns.path_params)
# TODO: remove deprecated @beacon_query_params
|> Component.assign(:beacon_query_params, beacon_assigns.query_params)
|> Component.assign(:beacon, beacon_assigns)

{:noreply, push_event(socket, "beacon:page-updated", %{meta_tags: Beacon.Web.DataSource.meta_tags(socket.assigns)})}
end
end

@doc false
Expand Down
19 changes: 19 additions & 0 deletions test/beacon_web/live/page_live_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ defmodule Beacon.Web.Live.PageLiveTest do
@beacon.query_params=<%= @beacon.query_params["query"] %>
<.page_link path="/about">go_to_about_page</.page_link>
<.link patch={"#{Beacon.BeaconTest.Endpoint.url()}/other"}>go_to_other_site</.link>
<.form :let={f} for={%{}} as={:greeting} phx-submit="hello">
Name: <%= text_input f, :name %>
Expand Down Expand Up @@ -131,6 +132,14 @@ defmodule Beacon.Web.Live.PageLiveTest do
meta_tags: nil
)

beacon_published_page_fixture(
site: :not_booted,
path: "/",
template: """
<h1><%= @beacon.site %></h1>
"""
)

[layout: layout]
end

Expand All @@ -157,6 +166,16 @@ defmodule Beacon.Web.Live.PageLiveTest do
|> render_click() =~ "about_page"
end

test "patch to another site resets site data", %{conn: conn} do
{:ok, view, _html} = live(conn, "/home/hello")

view
|> element("a", "go_to_other_site")
|> render_click()

assert has_element?(view, "h1", "not_booted")
end

describe "meta tags" do
test "merge layout, page, and site", %{conn: conn} do
{:ok, _view, html} = live(conn, "/home/hello")
Expand Down
1 change: 1 addition & 0 deletions test/support/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ defmodule Beacon.BeaconTest.Router do

scope "/" do
pipe_through :browser
beacon_site "/other", site: :not_booted
beacon_site "/", site: :my_site
end
end

0 comments on commit 7b04a89

Please sign in to comment.