Skip to content

Commit 737378e

Browse files
authored
build: upgrade to Phoenix LiveView 1.0 (#566)
1 parent 876db45 commit 737378e

File tree

88 files changed

+964
-916
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+964
-916
lines changed

.formatter.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
import_deps: [:ecto, :phoenix],
3-
plugins: [Phoenix.LiveView.HTMLFormatter, TailwindFormatter.MultiFormatter, DoctestFormatter],
3+
plugins: [TailwindFormatter, DoctestFormatter, Phoenix.LiveView.HTMLFormatter],
44
heex_line_length: 300,
55
inputs: [
66
"*.{heex,ex,exs}",

.github/workflows/style.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ jobs:
1616

1717
strategy:
1818
matrix:
19-
otp: [26.x]
20-
elixir: [1.14.x]
19+
otp: [27.x]
20+
elixir: [1.17.x]
2121

2222
steps:
2323
- name: ☁️ Checkout repository

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ jobs:
2323

2424
strategy:
2525
matrix:
26-
otp: [26.x]
27-
elixir: [1.14.x]
26+
otp: [27.x]
27+
elixir: [1.17.x]
2828

2929
services:
3030
db:

.tool-versions

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
elixir 1.14.4-otp-26
2-
erlang 26.1.1
1+
elixir 1.17.2-otp-27
2+
erlang 27.0

assets/js/app.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {LiveSocket} from "phoenix_live_view"
2525
import "../vendor/alpine.js";
2626
import topbar from "../vendor/topbar"
2727
import { QrScanner, InitSorting, StickyScroll, ScrollToTop } from "./hooks";
28+
import phxFeedbackDom from "./shims/phx_feedback_dom.js"
2829

2930
let Hooks = {
3031
QrScanner: QrScanner,
@@ -37,16 +38,13 @@ let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("
3738
let liveSocket = new LiveSocket("/live", Socket, {
3839
params: { _csrf_token: csrfToken },
3940
hooks: Hooks,
40-
dom: {
41+
dom: phxFeedbackDom({
4142
onBeforeElUpdated(from, to) {
42-
// If the element we are updating is an Alpine component...
4343
if (from._x_dataStack) {
44-
// Then temporarily clone it (with it's data) to the "to" element.
45-
// This should simulate LiveView being aware of Alpine changes.
4644
window.Alpine.clone(from, to);
4745
}
48-
},
49-
},
46+
}
47+
}),
5048
});
5149

5250
// Show progress bar on live navigation and form submits
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// https://gist.github.com/chrismccord/c4c60328c6ac5ec29e167bb115315d82
2+
// maintain backwards compatibility of phx-feedback-for, which was removed in LiveView 1.0
3+
// find all phx-feedback-for containers and show/hide phx-no-feedback class based on used inputs
4+
import {isUsedInput} from "phoenix_live_view"
5+
6+
let resetFeedbacks = (container, feedbacks) => {
7+
feedbacks = feedbacks || Array.from(container.querySelectorAll("[phx-feedback-for]"))
8+
.map(el => [el, el.getAttribute("phx-feedback-for")])
9+
10+
feedbacks.forEach(([feedbackEl, name]) => {
11+
let query = `[name="${name}"], [name="${name}[]"]`
12+
let isUsed = Array.from(container.querySelectorAll(query)).find(input => isUsedInput(input))
13+
if(isUsed || !feedbackEl.hasAttribute("phx-feedback-for")){
14+
feedbackEl.classList.remove("phx-no-feedback")
15+
} else {
16+
feedbackEl.classList.add("phx-no-feedback")
17+
}
18+
})
19+
}
20+
21+
export default phxFeedbackDom = (dom) => {
22+
window.addEventListener("reset", e => resetFeedbacks(document))
23+
let feedbacks
24+
let submitPending = false
25+
let inputPending = false
26+
window.addEventListener("submit", e => submitPending = e.target)
27+
window.addEventListener("input", e => inputPending = e.target)
28+
// extend provided dom options with our own.
29+
// accumulate phx-feedback-for containers for each patch and reset feedbacks when patch ends
30+
return {
31+
onPatchStart(container){
32+
feedbacks = []
33+
dom.onPatchStart && dom.onPatchStart(container)
34+
},
35+
onNodeAdded(node){
36+
if(node.hasAttribute && node.hasAttribute("phx-feedback-for")){
37+
feedbacks.push([node, node.getAttribute("phx-feedback-for")])
38+
}
39+
dom.onNodeAdded && dom.onNodeAdded(node)
40+
},
41+
onBeforeElUpdated(from, to){
42+
let fromFor = from.getAttribute("phx-feedback-for")
43+
let toFor = to.getAttribute("phx-feedback-for")
44+
if(fromFor || toFor){ feedbacks.push([from, fromFor || toFor], [to, toFor || fromFor]) }
45+
46+
dom.onBeforeElUpdated && dom.onBeforeElUpdated(from, to)
47+
},
48+
onPatchEnd(container){
49+
resetFeedbacks(container, feedbacks)
50+
// we might not find some feedback nodes if they are skipped in the patch
51+
// therefore we explicitly reset feedbacks for all nodes when the patch
52+
// follows a submit or input event
53+
if(inputPending || submitPending){
54+
resetFeedbacks(container)
55+
inputPending = null
56+
submitPending = null
57+
}
58+
dom.onPatchEnd && dom.onPatchEnd(container)
59+
}
60+
}
61+
}

config/dev.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,9 @@ config :phoenix, :stacktrace_depth, 20
7777
# Initialize plugs at runtime for faster development compilation
7878
config :phoenix, :plug_init_mode, :runtime
7979

80+
config :phoenix_live_view,
81+
# Include HEEx debug annotations as HTML comments in rendered markup
82+
debug_heex_annotations: true
83+
8084
# Other configurations for the app
8185
config :pdf_generator, raise_on_missing_wkhtmltopdf_binary: false

lib/atomic/organizations/collaborator.ex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ defmodule Atomic.Organizations.Collaborator do
1919
order_by: [:inserted_at],
2020
order_directions: [:desc]
2121
},
22-
join_fields: [
23-
collaborator_name: [binding: :user, field: :name, path: [:user, :name]]
22+
adapter_opts: [
23+
join_fields: [
24+
collaborator_name: [binding: :user, field: :name, path: [:user, :name]]
25+
]
2426
]
2527
}
2628

lib/atomic_web.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,9 @@ defmodule AtomicWeb do
9898

9999
defp view_helpers do
100100
quote do
101-
# Use all HTML functionality (forms, tags, etc)
102-
use Phoenix.HTML
101+
import Phoenix.HTML
102+
import Phoenix.HTML.Form
103+
use PhoenixHTMLHelpers
103104

104105
# Import LiveView and .heex helpers (<.link>, <.form>, etc)
105106
import Phoenix.LiveView.Helpers

lib/atomic_web/components/activity.ex

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,19 @@ defmodule AtomicWeb.Components.Activity do
2121
<object>
2222
<.link navigate={~p"/organizations/#{@activity.organization.id}"}>
2323
<span class="text-sm font-medium text-zinc-900 hover:underline focus:outline-none">
24-
<%= @activity.organization.name %>
24+
{@activity.organization.name}
2525
</span>
2626
</.link>
2727
</object>
2828
<p class="text-sm text-zinc-500">
2929
<span class="sr-only">Published on</span>
30-
<time><%= relative_datetime(@activity.inserted_at) %></time>
30+
<time>{relative_datetime(@activity.inserted_at)}</time>
3131
</p>
3232
</div>
3333
</div>
34-
<h2 class="mt-3 text-base font-semibold text-zinc-900"><%= @activity.title %></h2>
34+
<h2 class="mt-3 text-base font-semibold text-zinc-900">{@activity.title}</h2>
3535
<div class="overflow-hidden break-words text-justify text-sm text-zinc-700">
36-
<p><%= maybe_slice_string(@activity.description, 300) %></p>
36+
<p>{maybe_slice_string(@activity.description, 300)}</p>
3737
</div>
3838
<!-- Image -->
3939
<%= if @activity.image do %>
@@ -47,21 +47,21 @@ defmodule AtomicWeb.Components.Activity do
4747
<span class="inline-flex items-center text-sm">
4848
<span class="inline-flex space-x-2 text-zinc-400">
4949
<.icon name="hero-calendar-solid" class="mr-1.5 h-5 w-5 flex-shrink-0 text-zinc-400" />
50-
<span class="font-medium text-zinc-900"><%= pretty_display_date(@activity.start) %></span>
50+
<span class="font-medium text-zinc-900">{pretty_display_date(@activity.start)}</span>
5151
<span class="sr-only">starting in</span>
5252
</span>
5353
</span>
5454
<span class="inline-flex items-center text-sm">
5555
<span class="inline-flex space-x-2 text-zinc-400">
5656
<.icon name="hero-user-group-solid" class="size-5" />
57-
<span class="font-medium text-zinc-900"><%= @activity.enrolled %>/<%= @activity.maximum_entries %></span>
57+
<span class="font-medium text-zinc-900">{@activity.enrolled}/{@activity.maximum_entries}</span>
5858
<span class="sr-only text-zinc-400">enrollments</span>
5959
</span>
6060
</span>
6161
<span class="inline-flex items-center text-sm">
6262
<span class="inline-flex space-x-2 text-zinc-400">
6363
<.icon name="hero-map-pin-solid" class="size-5" />
64-
<span class="font-medium text-zinc-900"><%= @activity.location.name %></span>
64+
<span class="font-medium text-zinc-900">{@activity.location.name}</span>
6565
<span class="sr-only">location</span>
6666
</span>
6767
</span>

0 commit comments

Comments
 (0)