Skip to content

Commit

Permalink
dump work
Browse files Browse the repository at this point in the history
  • Loading branch information
rktjmp committed Nov 11, 2022
1 parent c6d2315 commit d7ba32c
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 22 deletions.
1 change: 1 addition & 0 deletions .formatter.exs
@@ -1,4 +1,5 @@
[
plugins: [Phoenix.LiveView.HTMLFormatter],
import_deps: [:ecto, :phoenix],
inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"],
subdirectories: ["priv/*/migrations"]
Expand Down
40 changes: 40 additions & 0 deletions README.md
@@ -0,0 +1,40 @@
# Necromancy

Using Phoenix.LiveView inplace of classical controllers, optionally
resurrecting them from the dead.

## Reasoning

- A consistent developer experience.

All your views are constructed in the same manner, with a mount, handle_params,
etc process flow. The "template render flow" is the same.

- But not all views really need to be "live"

Some static [sic] pages have no sever state to update. These could/can just be
regular Phoenix controller actions.

- But, but some views might warrant starting dead and then *becoming* live.

## Reasons against

- Kinda weird
- Added complexity with expecting a liveview to never connect?
- Phoenix 1.7 *may* unify how templates are put together with the new
templating engine?

## Direct usecase

All pages show content, with a "live chat/form" pop-out. The live-chat should
integrate with the current page - it should not redirect.

## Methods

- Show button, on click spawn iframe with liveview in it, let this do the fancy
work.
- Simpler, no hacks
- iframes ugly?

- Dead to live
- render view as dead-liveview, on click connect livesocket or import livesocket code
59 changes: 47 additions & 12 deletions assets/js/app.js
Expand Up @@ -26,20 +26,55 @@ import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"

topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
var timeout = null
window.addEventListener("phx:page-loading-start", (info) => {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
timeout = setTimeout(topbar.show, 200)
})
window.addEventListener("phx:page-loading-stop", (info) => {
topbar.hide()
if (timeout) {
clearTimeout(timeout)
timeout = null
}
})

// seems that its preferable to always construct the livesocket but not connect
// it, vs constructing it on demand as redispatched click events don't seem to
// be caught.
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
liveSocket.enableDebug()

// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", info => topbar.show())
window.addEventListener("phx:page-loading-stop", info => topbar.hide())

// connect if there are any LiveViews on the page
liveSocket.connect()
let bindConnectForEvent = (el, eventType) => {
el.addEventListener(eventType, (e) => {
// if we're not connected, connect and then refire the same event incase
// we're bound to a phx-click etc.
if (!liveSocket.isConnected()) {
window.addEventListener("phx:page-loading-stop", () => {
el.dispatchEvent(e)
}, {once: true})
liveSocket.connect()
}})
}

// expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket
window.addEventListener("DOMContentLoaded", () => {
// This is a generic connect, you could use click, hover, etc.
// <button data-connect-liveview="click">...
// will connect the livesocket on click. You could also do
// <form data-connect-liveview="input">...
// but this is not redispatched cleanly, you'd instead have to redispatched
// against the actual input field.
document.querySelectorAll("[data-connect-liveview]").forEach((el) => {
bindConnectForEvent(el, el.dataset.connectLiveview)
})

// Or we could bind to known phx attribute to always resume
document.querySelectorAll("[phx-click]").forEach((el) => {
bindConnectForEvent(el, "click")
})
})
3 changes: 3 additions & 0 deletions lib/necromancy_web.ex
Expand Up @@ -47,6 +47,8 @@ defmodule NecromancyWeb do
use Phoenix.LiveView,
layout: {NecromancyWeb.LayoutView, "live.html"}

use Phoenix.Component

unquote(view_helpers())
end
end
Expand Down Expand Up @@ -91,6 +93,7 @@ defmodule NecromancyWeb do

# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
import Phoenix.LiveView.Helpers
import Phoenix.Component

# Import basic rendering functionality (render, render_layout, etc)
import Phoenix.View
Expand Down
127 changes: 127 additions & 0 deletions lib/necromancy_web/live/necro_live.ex
@@ -0,0 +1,127 @@
defmodule NecromancyWeb.NecroLive do
defmodule Form do
use Ecto.Schema

embedded_schema do
field(:name)
end

def changeset(params) do
%Form{}
|> Ecto.Changeset.cast(params, [:name])
|> Ecto.Changeset.validate_required([:name])
|> Ecto.Changeset.validate_format(:name, ~r/[0-9] [a-z]+/,
message: "Name must match 0-9 a-Z"
)
end
end

use NecromancyWeb, :live_view

def mount(_params, _session, socket) do
socket =
assign(
socket,
event_ran?: false,
connected?: connected?(socket),
changeset: Form.changeset(%{}),
task: nil,
bytes: nil
)

{:ok, socket}
end

def handle_event("start_process", _, socket) do
task =
Task.async(fn ->
Process.sleep(3000)
{:ok, :crypto.strong_rand_bytes(64)}
end)

socket = assign(socket, task: task)
{:noreply, socket}
end

# you can match on task ref if you have a reason to
def handle_info({ref, {:ok, bytes}}, %{assigns: %{task: %{ref: ref}}} = socket) do
socket = assign(socket, task: nil, bytes: bytes)
{:noreply, socket}
end

def handle_info({:DOWN, _ref, :process, _pid, _reason}, socket) do
# probably do something smart here
{:noreply, socket}
end

def handle_event("run_event", _, socket) do
socket = assign(socket, event_ran?: true)
{:noreply, socket}
end

def handle_event("validate", %{"form" => params}, socket) do
changeset =
Form.changeset(params)
|> Map.put(:action, :validate)

socket = assign(socket, changeset: changeset)
{:noreply, socket}
end

def handle_event("submit", %{"form" => params}, socket) do
changeset =
Form.changeset(params)
|> Map.put(:action, :validate)

socket = assign(socket, changeset: changeset)

socket =
case changeset do
%{valid?: true} ->
put_flash(socket, :info, "nice one!")

_ ->
put_flash(socket, :error, "dang it!")
end

{:noreply, socket}
end

def render(assigns) do
~H"""
<section>
<p :if={@connected?}>
I am ALIVE. <button phx-click="start_process">Run long task</button>
<%= inspect(@task) %>
<%= inspect(@bytes) %>
</p>
<p :if={not @connected?}>
I am DEAD.
</p>
<p :if={not @connected?}>
<button type="button" data-connect-liveview="click">Just Connect</button>
</p>
<button type="button" phx-click="run_event">
Run an action
</button>
<p>Event has been run?: <%= @event_ran? %></p>
Access form to connect
<.form
:let={f}
data-connect-liveview="input"
for={@changeset}
phx-change="validate"
phx-submit="submit"
phx-page-loading="false"
>
<%= text_input(f, :name) %>
<%= error_tag(f, :name) %>
</.form>
</section>
"""
end
end
2 changes: 1 addition & 1 deletion lib/necromancy_web/router.ex
Expand Up @@ -17,7 +17,7 @@ defmodule NecromancyWeb.Router do
scope "/", NecromancyWeb do
pipe_through :browser

get "/", PageController, :index
live "/", NecroLive, :index
end

# Other scopes may use custom stacks.
Expand Down
6 changes: 3 additions & 3 deletions mix.exs
Expand Up @@ -7,7 +7,7 @@ defmodule Necromancy.MixProject do
version: "0.1.0",
elixir: "~> 1.12",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:gettext] ++ Mix.compilers(),
compilers: [] ++ Mix.compilers(),
start_permanent: Mix.env() == :prod,
aliases: aliases(),
deps: deps()
Expand Down Expand Up @@ -39,9 +39,9 @@ defmodule Necromancy.MixProject do
{:ecto_sqlite3, ">= 0.0.0"},
{:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.17.5"},
{:phoenix_live_view, "~> 0.18.0"},
{:floki, ">= 0.30.0", only: :test},
{:phoenix_live_dashboard, "~> 0.6"},
{:phoenix_live_dashboard, "~> 0.7"},
{:esbuild, "~> 0.4", runtime: Mix.env() == :dev},
{:swoosh, "~> 1.3"},
{:telemetry_metrics, "~> 0.6"},
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Expand Up @@ -21,9 +21,9 @@
"phoenix": {:hex, :phoenix, "1.6.14", "57678366dc1d5bad49832a0fc7f12c2830c10d3eacfad681bfe9602cd4445f04", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d48c0da00b3d4cd1aad6055387917491af9f6e1f1e96cedf6c6b7998df9dba26"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
"phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.5", "1495bb014be12c9a9252eca04b9af54246f6b5c1e4cd1f30210cd00ec540cf8e", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.7", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "ef4fa50dd78364409039c99cf6f98ab5209b4c5f8796c17f4db118324f0db852"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.1", "b0bf8f3348dec4910907a2ad1453e642f6fe4d444376c1c9b26222d63c73cf97", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "b6c5d744bf4b40692b1b361d3608bdfd05aeab83e17c7bc217d730f007f31abf"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.17.12", "74f4c0ad02d7deac2d04f50b52827a5efdc5c6e7fac5cede145f5f0e4183aedc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.0 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "af6dd5e0aac16ff43571f527a8e0616d62cb80b10eb87aac82170243e50d99c8"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.2", "635cf07de947235deb030cd6b776c71a3b790ab04cebf526aa8c879fe17c7784", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6 or ~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da287a77327e996cc166e4c440c3ad5ab33ccdb151b91c793209b39ebbce5b75"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
"phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"},
"plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"},
Expand Down
8 changes: 4 additions & 4 deletions test/necromancy_web/controllers/page_controller_test.exs
@@ -1,8 +1,8 @@
defmodule NecromancyWeb.PageControllerTest do
use NecromancyWeb.ConnCase

test "GET /", %{conn: conn} do
conn = get(conn, "/")
assert html_response(conn, 200) =~ "Welcome to Phoenix!"
end
# test "GET /", %{conn: conn} do
# conn = get(conn, "/")
# assert html_response(conn, 200) =~ "Welcome to Phoenix!"
# end
end

0 comments on commit d7ba32c

Please sign in to comment.