Skip to content

Commit 2dd41b7

Browse files
committed
stream_insert update_only aka stream_update
Fixes #2690. Relates to: https://elixirforum.com/t/add-stream-update-to-liveview-streams/68107 Work in progress, no tests yet.
1 parent e73735c commit 2dd41b7

6 files changed

Lines changed: 31 additions & 11 deletions

File tree

assets/js/phoenix_live_view/dom_patch.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ export default class DOMPatch {
125125
}
126126
},
127127
onBeforeNodeAdded: (el) => {
128+
// don't add update_only nodes if they did not already exist
129+
if(this.getStreamInsert(el)?.updateOnly && !this.streamComponentRestore[el.id]){
130+
return false
131+
}
132+
128133
DOM.maintainPrivateHooks(el, el, phxViewportTop, phxViewportBottom)
129134
this.trackBefore("added", el)
130135

@@ -278,8 +283,8 @@ export default class DOMPatch {
278283

279284
liveSocket.time("morphdom", () => {
280285
this.streams.forEach(([ref, inserts, deleteIds, reset]) => {
281-
inserts.forEach(([key, streamAt, limit]) => {
282-
this.streamInserts[key] = {ref, streamAt, limit, reset}
286+
inserts.forEach(([key, streamAt, limit, updateOnly]) => {
287+
this.streamInserts[key] = {ref, streamAt, limit, reset, updateOnly}
283288
})
284289
if(reset !== undefined){
285290
DOM.all(container, `[${PHX_STREAM_REF}="${ref}"]`, child => {

lib/phoenix_live_view.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1843,6 +1843,9 @@ defmodule Phoenix.LiveView do
18431843
here as well in order to be enforced. See `stream/4` for more information on
18441844
limiting streams.
18451845
1846+
* `:update_only` - A boolean to only update the item in the stream. If the item does not
1847+
exist on the client, it will not be inserted. Defaults to `false`.
1848+
18461849
## Examples
18471850
18481851
Imagine you define a stream on mount with a single item:
@@ -1892,8 +1895,9 @@ defmodule Phoenix.LiveView do
18921895
def stream_insert(%Socket{} = socket, name, item, opts \\ []) do
18931896
at = Keyword.get(opts, :at, -1)
18941897
limit = Keyword.get(opts, :limit)
1898+
update_only = Keyword.get(opts, :update_only, false)
18951899

1896-
update_stream(socket, name, &LiveStream.insert_item(&1, item, at, limit))
1900+
update_stream(socket, name, &LiveStream.insert_item(&1, item, at, limit, update_only))
18971901
end
18981902

18991903
@doc """

lib/phoenix_live_view/engine.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ defmodule Phoenix.LiveView.Comprehension do
8484

8585
@doc false
8686
def __annotate__(comprehension, %Phoenix.LiveView.LiveStream{} = stream) do
87-
inserts = for {id, at, _item, limit} <- stream.inserts, do: [id, at, limit]
87+
inserts =
88+
for {id, at, _item, limit, update_only} <- stream.inserts, do: [id, at, limit, update_only]
89+
8890
data = [stream.ref, inserts, stream.deletes]
8991

9092
if stream.reset? do

lib/phoenix_live_view/live_stream.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ defmodule Phoenix.LiveView.LiveStream do
2525
# with manually calling stream_insert multiple times, as stream_insert prepends.
2626
items_list =
2727
for item <- items, reduce: [] do
28-
items -> [{dom_id.(item), -1, item, opts[:limit]} | items]
28+
items -> [{dom_id.(item), -1, item, opts[:limit], opts[:update_only]} | items]
2929
end
3030

3131
%LiveStream{
@@ -64,10 +64,10 @@ defmodule Phoenix.LiveView.LiveStream do
6464
%{stream | deletes: [dom_id | stream.deletes]}
6565
end
6666

67-
def insert_item(%LiveStream{} = stream, item, at, limit) do
67+
def insert_item(%LiveStream{} = stream, item, at, limit, update_only) do
6868
item_id = stream.dom_id.(item)
6969

70-
%{stream | inserts: [{item_id, at, item, limit} | stream.inserts]}
70+
%{stream | inserts: [{item_id, at, item, limit, update_only} | stream.inserts]}
7171
end
7272

7373
defimpl Enumerable, for: LiveStream do
@@ -106,7 +106,7 @@ defmodule Phoenix.LiveView.LiveStream do
106106
defp do_reduce(list, {:suspend, acc}, fun), do: {:suspended, acc, &do_reduce(list, &1, fun)}
107107
defp do_reduce([], {:cont, acc}, _fun), do: {:done, acc}
108108

109-
defp do_reduce([{dom_id, _at, item, _limit} | tail], {:cont, acc}, fun) do
109+
defp do_reduce([{dom_id, _at, item, _limit, _update_only} | tail], {:cont, acc}, fun) do
110110
do_reduce(tail, fun.({dom_id, item}, acc), fun)
111111
end
112112

lib/phoenix_live_view/test/dom.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,8 @@ defmodule Phoenix.LiveViewTest.DOM do
502502

503503
streamInserts =
504504
Enum.reduce(streams, %{}, fn %{ref: ref, inserts: inserts}, acc ->
505-
Enum.reduce(inserts, acc, fn [id, stream_at, limit], acc ->
505+
# TODO: support update_only in LiveViewTest
506+
Enum.reduce(inserts, acc, fn [id, stream_at, limit, _update_only], acc ->
506507
Map.put(acc, id, %{ref: ref, stream_at: stream_at, limit: limit})
507508
end)
508509
end)

test/phoenix_live_view/live_stream_test.exs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,20 @@ defmodule Phoenix.LiveView.LiveStreamTest do
1313

1414
test "default dom_id" do
1515
stream = LiveStream.new(:users, 0, [%{id: 1}, %{id: 2}], [])
16-
assert stream.inserts == [{"users-2", -1, %{id: 2}, nil}, {"users-1", -1, %{id: 1}, nil}]
16+
17+
assert stream.inserts == [
18+
{"users-2", -1, %{id: 2}, nil, nil},
19+
{"users-1", -1, %{id: 1}, nil, nil}
20+
]
1721
end
1822

1923
test "custom dom_id" do
2024
stream = LiveStream.new(:users, 0, [%{name: "u1"}, %{name: "u2"}], dom_id: &"u-#{&1.name}")
21-
assert stream.inserts == [{"u-u2", -1, %{name: "u2"}, nil}, {"u-u1", -1, %{name: "u1"}, nil}]
25+
26+
assert stream.inserts == [
27+
{"u-u2", -1, %{name: "u2"}, nil, nil},
28+
{"u-u1", -1, %{name: "u1"}, nil, nil}
29+
]
2230
end
2331

2432
test "default dom_id without struct or map with :id" do

0 commit comments

Comments
 (0)