Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Live reloading LiveViews without a hard refresh #2323

Merged
merged 5 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions lib/phoenix_live_view/channel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,19 @@ defmodule Phoenix.LiveView.Channel do
handle_redirect(state, command, flash, nil)
end

def handle_info({:phoenix_live_reload, _topic, _changed_file}, %{socket: socket} = state) do
Phoenix.CodeReloader.reload(socket.endpoint)

new_socket =
Enum.reduce(socket.assigns, socket, fn {key, val}, socket ->
Utils.force_assign(socket, key, val)
end)

handle_changed(state, new_socket, nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tricky because if the user changed mount or handle_params, those wouldn't be picked up. I partially feel the best way to go around this is to send a client a refresh and the client performs a full remount. Probably doing the changes in #2326.


{:noreply, state}
bemesa21 marked this conversation as resolved.
Show resolved Hide resolved
end

def handle_info(msg, %{socket: socket} = state) do
msg
|> view_handle_info(socket)
Expand Down Expand Up @@ -1008,6 +1021,7 @@ defmodule Phoenix.LiveView.Channel do
|> build_state(phx_socket)
|> maybe_call_mount_handle_params(router, url, params)
|> reply_mount(from, verified, route)
|> maybe_subscribe_to_live_reload()

{:error, :noproc} ->
GenServer.reply(from, {:error, %{reason: "stale"}})
Expand Down Expand Up @@ -1308,4 +1322,18 @@ defmodule Phoenix.LiveView.Channel do
_ -> nil
end
end

defp maybe_subscribe_to_live_reload({:noreply, state}) do
live_reload_config = state.socket.endpoint.config(:live_reload)

for {topic, _patterns} <- live_reload_config[:notify] || [] do
bemesa21 marked this conversation as resolved.
Show resolved Hide resolved
topic
|> to_string()
|> state.socket.endpoint.subscribe()
end

{:noreply, state}
end

defp maybe_subscribe_to_live_reload(response), do: response
end
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ defmodule Phoenix.LiveView.MixProject do
{:floki, "~> 0.30.0", only: :test},
{:ex_doc, "~> 0.28", only: :docs},
{:makeup_eex, ">= 0.1.1", only: :docs},
{:html_entities, ">= 0.0.0", only: :test}
{:html_entities, ">= 0.0.0", only: :test},
{:phoenix_live_reload, path: "../phoenix_live_reload", only: :test, override: true}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need to point to hex once we release live reload

chrismccord marked this conversation as resolved.
Show resolved Hide resolved
]
end

Expand Down
8 changes: 7 additions & 1 deletion test/phoenix_live_view/integrations/assigns_test.exs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
defmodule Phoenix.LiveView.AssignsTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false
import Plug.Conn
import Phoenix.ConnTest

import Phoenix.LiveViewTest
alias Phoenix.LiveViewTest.Endpoint
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

setup do
{:ok, conn: Plug.Test.init_test_session(Phoenix.ConnTest.build_conn(), %{})}
end
Expand Down
8 changes: 7 additions & 1 deletion test/phoenix_live_view/integrations/collocated_test.exs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
defmodule Phoenix.LiveView.CollocatedTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false
import Phoenix.ConnTest

import Phoenix.LiveViewTest
alias Phoenix.LiveViewTest.{Endpoint, CollocatedLive, CollocatedComponent}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

test "supports collocated views" do
{:ok, view, html} = live_isolated(build_conn(), CollocatedLive)
assert html =~ "Hello collocated world from live!\n</div>"
Expand Down
10 changes: 8 additions & 2 deletions test/phoenix_live_view/integrations/connect_test.exs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
defmodule Phoenix.LiveView.ConnectTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false
import Phoenix.LiveViewTest
import Phoenix.ConnTest
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Phoenix.LiveViewTest.Endpoint

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

describe "connect_params" do
test "can be read on mount" do
{:ok, live, _html} =
Expand Down Expand Up @@ -40,4 +46,4 @@ defmodule Phoenix.LiveView.ConnectTest do
assert_html.(render(live))
end
end
end
end
8 changes: 7 additions & 1 deletion test/phoenix_live_view/integrations/elements_test.exs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
defmodule Phoenix.LiveView.ElementsTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false
import Phoenix.ConnTest

import Phoenix.LiveViewTest
alias Phoenix.LiveViewTest.{Endpoint}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

Expand All @@ -15,6 +16,11 @@ defmodule Phoenix.LiveView.ElementsTest do
view |> element("#component-last-event") |> render() |> HtmlEntities.decode()
end

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

setup do
conn = Phoenix.ConnTest.build_conn()
{:ok, live, _} = live(conn, "/elements")
Expand Down
5 changes: 3 additions & 2 deletions test/phoenix_live_view/integrations/event_test.exs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
defmodule Phoenix.LiveView.EventTest do
use ExUnit.Case
use ExUnit.Case, async: false

import Phoenix.ConnTest
import Phoenix.LiveViewTest

alias Phoenix.{Component, LiveView}
alias Phoenix.LiveViewTest.{Endpoint}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

setup_all do
ExUnit.CaptureLog.capture_log(fn -> Endpoint.start_link() end)
Helpers.start_endpoint(@endpoint)
:ok
end

Expand Down
8 changes: 7 additions & 1 deletion test/phoenix_live_view/integrations/flash_test.exs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
defmodule Phoenix.LiveView.FlashIntegrationTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false
import Phoenix.ConnTest

import Phoenix.LiveViewTest
alias Phoenix.LiveView
alias Phoenix.LiveViewTest.{Endpoint, Router}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

setup do
conn =
Phoenix.ConnTest.build_conn(:get, "http://www.example.com/", nil)
Expand Down
8 changes: 7 additions & 1 deletion test/phoenix_live_view/integrations/hooks_test.exs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
defmodule Phoenix.LiveView.HooksTest do
use ExUnit.Case
use ExUnit.Case, async: false

import Phoenix.ConnTest
import Phoenix.LiveViewTest

alias Phoenix.Component
alias Phoenix.LiveViewTest.{Endpoint, HooksLive}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

setup do
{:ok, conn: Plug.Test.init_test_session(build_conn(), %{})}
end
Expand Down
8 changes: 7 additions & 1 deletion test/phoenix_live_view/integrations/layout_test.exs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
defmodule Phoenix.LiveView.LayoutTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false
import Phoenix.ConnTest

import Phoenix.LiveViewTest
alias Phoenix.LiveViewTest.{Endpoint, LayoutView}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

setup config do
{:ok,
conn: Plug.Test.init_test_session(Phoenix.ConnTest.build_conn(), config[:session] || %{})}
Expand Down
8 changes: 7 additions & 1 deletion test/phoenix_live_view/integrations/live_components_test.exs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
defmodule Phoenix.LiveView.LiveComponentsTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false
import Phoenix.ConnTest

import Phoenix.LiveViewTest
alias Phoenix.LiveViewTest.{Endpoint, DOM, StatefulComponent}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint
@moduletag session: %{names: ["chris", "jose"], from: nil}

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

setup config do
{:ok,
conn: Plug.Test.init_test_session(Phoenix.ConnTest.build_conn(), config[:session] || %{})}
Expand Down
45 changes: 45 additions & 0 deletions test/phoenix_live_view/integrations/live_reload_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
defmodule Phoenix.LiveView.LiveReloadTest do
use ExUnit.Case, async: false

import Phoenix.ConnTest
import Phoenix.ChannelTest
import Phoenix.LiveViewTest

alias Phoenix.LiveReloader
alias Phoenix.LiveReloader.Channel
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Helpers.Endpoint
@pubsub Phoenix.LiveView.PubSub

setup config do
ExUnit.CaptureLog.capture_log(fn ->
start_supervised!(@endpoint)

{:ok, _} =
Supervisor.start_link([Phoenix.PubSub.child_spec(name: @pubsub)],
strategy: :one_for_one
)
end)

{:ok, _, socket} =
LiveReloader.Socket |> socket() |> subscribe_and_join(Channel, "phoenix:live_reload", %{})
conn = Plug.Test.init_test_session(build_conn(), config[:session] || %{})
{:ok, conn: conn, socket: socket}
end

test "LiveView renders again when the phoenix_live_reload is received", %{conn: conn, socket: socket} do
Phoenix.PubSub.subscribe(@pubsub, "liveview")

Application.put_env(:phoenix_live_view, :vsn, 1)
{:ok, lv, _html} = live(conn, "/live-reload")
assert render(lv) =~ "<div>Version 1</div>"

send(socket.channel_pid, {:file_event, self(), {"lib/test_auth_web/live/user_live.ex", :created}})
Application.put_env(:phoenix_live_view, :vsn, 2)

assert_receive {:phoenix_live_reload, :liveview, "lib/test_auth_web/live/user_live.ex"}
assert render(lv) =~ "<div>Version 2</div>"
end

end
8 changes: 7 additions & 1 deletion test/phoenix_live_view/integrations/live_view_test.exs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
defmodule Phoenix.LiveView.LiveViewTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false

import Phoenix.ConnTest
import Phoenix.LiveViewTest

alias Phoenix.HTML
alias Phoenix.LiveView
alias Phoenix.LiveViewTest.{Endpoint, DOM}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

setup config do
{:ok, conn: Plug.Test.init_test_session(build_conn(), config[:session] || %{})}
end
Expand Down
6 changes: 4 additions & 2 deletions test/phoenix_live_view/integrations/navigation_test.exs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
defmodule Phoenix.LiveView.NavigationTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false

import Phoenix.ConnTest
import Phoenix.LiveViewTest

alias Phoenix.LiveViewTest.{Endpoint, DOM}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

setup do
{:ok, conn: Plug.Test.init_test_session(build_conn(), %{})}
end

setup_all do
Endpoint.start_link()
Helpers.start_endpoint(@endpoint)
:ok
end

Expand Down
8 changes: 7 additions & 1 deletion test/phoenix_live_view/integrations/nested_test.exs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
defmodule Phoenix.LiveView.NestedTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false

import Plug.Conn
import Phoenix.ConnTest
import Phoenix.LiveViewTest

alias Phoenix.LiveView
alias Phoenix.LiveViewTest.{Endpoint, DOM, ClockLive, ClockControlsLive, LiveInComponent}
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

setup config do
{:ok, conn: Plug.Test.init_test_session(build_conn(), config[:session] || %{})}
end
Expand Down
6 changes: 6 additions & 0 deletions test/phoenix_live_view/integrations/telemetry_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ defmodule Phoenix.LiveView.TelemtryTest do

alias Phoenix.LiveView.Socket
alias Phoenix.LiveViewTest.Endpoint
alias Phoenix.LiveView.LiveReloadTestHelpers, as: Helpers

@endpoint Endpoint
@moduletag session: %{names: ["chris", "jose"], from: nil}

setup_all do
Helpers.start_endpoint(@endpoint)
:ok
end

setup config do
{:ok, conn: Plug.Test.init_test_session(build_conn(), config[:session] || %{})}
end
Expand Down
Loading