Skip to content

Commit 4c2e08b

Browse files
committed
add debugging streams with streams
1 parent 1194a82 commit 4c2e08b

File tree

9 files changed

+119
-318
lines changed

9 files changed

+119
-318
lines changed

dev/components.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ defmodule LiveDebuggerDev.Components do
88
{"/messages", "Messages"},
99
{"/embedded", "Embedded"},
1010
{"/embedded_in_controller", "EmbeddedInController"},
11-
{"/endless_crash_reload", "EndlessCrashReload"}
11+
{"/endless_crash_reload", "EndlessCrashReload"},
12+
{"/stream", "Stream"}
1213
]
1314

1415
attr(:routes, :list, default: @routes)

dev/live_views/stream.ex

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ defmodule LiveDebuggerDev.LiveViews.Stream do
2626
<.button phx-click="insert_at_index">Insert At Index 4</.button>
2727
<.button phx-click="delete_item">Delete Last</.button>
2828
<.button phx-click="reset_items">Reset Stream</.button>
29-
<.button phx-click="limit_stream">Limit Stream (5)</.button>
29+
<%!-- <.button phx-click="limit_stream">Limit Stream (5)</.button> --%>
3030
<.button phx-click="async_load">Async Load Items</.button>
3131
<.button phx-click="delete_both_last">Delete Last From Both Streams</.button>
3232
</.box>
@@ -192,11 +192,6 @@ defmodule LiveDebuggerDev.LiveViews.Stream do
192192
{:noreply, socket}
193193
end
194194

195-
@impl true
196-
def handle_info({:async_result, :items, {:ok, _}}, socket) do
197-
{:noreply, assign(socket, :async_loaded?, true)}
198-
end
199-
200195
@impl true
201196
def handle_event("delete_both_last", _params, socket) do
202197
last_item_id = socket.assigns.current_id - 1
@@ -223,4 +218,9 @@ defmodule LiveDebuggerDev.LiveViews.Stream do
223218

224219
{:noreply, socket}
225220
end
221+
222+
@impl true
223+
def handle_info({:async_result, :items, {:ok, _}}, socket) do
224+
{:noreply, assign(socket, :async_loaded?, true)}
225+
end
226226
end

lib/live_debugger/app/debugger/node_state/queries.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ defmodule LiveDebugger.App.Debugger.NodeState.Queries do
6363
end
6464
end
6565

66-
# def update_node_streams(_, stream_updates, current_stream_state_list) do
67-
# # StreamUtils.compute_diff([stream_updates], current_stream_state_list)
68-
# end
66+
def update_node_streams(_, stream_updates) do
67+
StreamUtils.compute_diff([stream_updates])
68+
end
6969

7070
defp get_component_assigns(components, %Phoenix.LiveComponent.CID{cid: cid}) do
7171
components
Lines changed: 62 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,27 @@
11
defmodule LiveDebugger.App.Debugger.NodeState.StreamUtils do
22
@moduledoc """
3-
Utilities for extracting and computing Phoenix LiveView stream diffs and state
3+
Utilities for extracting Phoenix LiveView stream diffs and state
44
from render traces.
55
"""
66

7-
alias LiveDebugger.App.Utils.TermDiffer
8-
alias LiveDebugger.App.Utils.TermDiffer.Diff
9-
alias LiveDebugger.App.Utils.TermParser
10-
11-
@empty_diff %Diff{type: :map, ins: %{}, del: %{}, diff: %{}}
12-
137
@doc """
148
Given a tuple of render traces, compute the base stream diff and stream state.
159
"""
1610
def build_initial_stream_diff({stream_updates, _trace}) do
1711
stream_traces = extract_stream_traces(stream_updates)
1812
stream_names = get_stream_names_map(stream_traces)
1913

20-
# base_diff = TermDiffer.diff(%{}, base_stream_state)
21-
22-
# {_, initial_term_tree} =
23-
# TermParser.update_by_diff(
24-
# TermParser.term_to_display_tree(%{}),
25-
# base_diff
26-
# )
27-
2814
fun_list = collect_updates(stream_traces)
29-
# stream_names = Enum.map(stream_traces, &elem(&1, 0))
15+
config_list = collect_config(stream_traces)
3016

31-
# dbg({inserts, deletes})
32-
# deletes = ensure_delete_keys_present(inserts, deletes)
17+
dbg(fun_list)
3318

34-
# computed_lists = reconstruct_stream_lists(inserts, deletes)
35-
# # dbg(computed_lists)
36-
# stream_state = build_stream_state_map(computed_lists)
37-
# # dbg(stream_state)
38-
# final_diff = build_diff_from_computed_lists(computed_lists)
39-
# dbg(final_diff)
19+
{fun_list, config_list, stream_names}
20+
end
4021

22+
def compute_diff(stream_updates) do
23+
stream_names = get_stream_names_map(stream_updates)
24+
fun_list = collect_updates(stream_updates)
4125
{fun_list, stream_names}
4226
end
4327

@@ -49,14 +33,6 @@ defmodule LiveDebugger.App.Debugger.NodeState.StreamUtils do
4933
|> Enum.reject(&Enum.empty?/1)
5034
end
5135

52-
defp infer_base_stream_state([]), do: %{}
53-
54-
defp infer_base_stream_state([first_trace | _]) do
55-
first_trace
56-
|> Enum.filter(fn {_, val} -> match?(%Phoenix.LiveView.LiveStream{}, val) end)
57-
|> Map.new(fn {key, _val} -> {key, []} end)
58-
end
59-
6036
defp get_stream_names_map(updates) do
6137
updates
6238
|> Enum.flat_map(fn update ->
@@ -76,21 +52,55 @@ defmodule LiveDebugger.App.Debugger.NodeState.StreamUtils do
7652
update_list
7753
end
7854

55+
defp collect_config(update_list) do
56+
update_list =
57+
update_list
58+
|> Enum.map(&flatten_stream_config(&1))
59+
# We only need the first render because config stays the same and cannot be changed
60+
|> Enum.take(1)
61+
|> List.flatten()
62+
63+
dbg(update_list)
64+
update_list
65+
end
66+
7967
def map_stream_entry_to_stream_function(%Phoenix.LiveView.LiveStream{
8068
name: name,
8169
inserts: inserts,
8270
deletes: deletes,
8371
reset?: reset?,
84-
consumable?: consumable?
72+
consumable?: _consumable?
8573
}) do
8674
[]
8775
|> maybe_add_reset(reset?, name)
88-
|> maybe_add_inserts(inserts, name)
76+
# Reverse to preserve the order of inserts
77+
|> maybe_add_inserts(Enum.reverse(inserts), name)
8978
|> maybe_add_deletes(deletes, name)
9079
end
9180

81+
def map_stream_entry_to_stream_config(
82+
%Phoenix.LiveView.LiveStream{
83+
name: name
84+
},
85+
config
86+
) do
87+
[]
88+
|> maybe_add_config(config, name)
89+
end
90+
91+
defp maybe_add_config(functions, nil, _name), do: functions
92+
93+
defp maybe_add_config(functions, [dom_id: fun], name) do
94+
functions ++ [fn socket -> Phoenix.LiveView.stream_configure(socket, name, dom_id: fun) end]
95+
end
96+
9297
defp maybe_add_reset(functions, true, name) do
93-
functions ++ [fn socket -> Phoenix.LiveView.stream(socket, name, [], reset: true) end]
98+
functions ++
99+
[
100+
fn socket ->
101+
Phoenix.LiveView.stream(socket, name, [], reset: true)
102+
end
103+
]
94104
end
95105

96106
defp maybe_add_reset(functions, false, _name), do: functions
@@ -108,7 +118,7 @@ defmodule LiveDebugger.App.Debugger.NodeState.StreamUtils do
108118
end
109119

110120
defp create_insert_functions(inserts, name) do
111-
Enum.map(inserts, fn {dom_id, at, element, limit, update?} ->
121+
Enum.map(inserts, fn {_dom_id, at, element, limit, update?} ->
112122
fn socket ->
113123
Phoenix.LiveView.stream_insert(socket, name, element,
114124
at: at,
@@ -121,151 +131,36 @@ defmodule LiveDebugger.App.Debugger.NodeState.StreamUtils do
121131

122132
defp create_delete_functions(deletes, name) do
123133
Enum.map(deletes, fn dom_id ->
124-
dbg(dom_id)
125-
126134
fn socket ->
127-
Phoenix.LiveView.stream_delete_by_dom_id(socket, name, dom_id)
135+
# Problem with same_id because ids are reused and element is updated and then deleted
136+
Process.send_after(self(), {:delete_stream_entry, name, dom_id, socket}, 1)
137+
socket
128138
end
129139
end)
130140
end
131141

132142
defp flatten_stream_updates(stream_entry) do
143+
dbg(stream_entry)
144+
133145
Enum.flat_map(stream_entry, fn
134-
{stream_name, %Phoenix.LiveView.LiveStream{} = stream} ->
135-
dbg(map_stream_entry_to_stream_function(stream))
146+
{_stream_name, %Phoenix.LiveView.LiveStream{} = stream} ->
136147
map_stream_entry_to_stream_function(stream)
137148

138149
_ ->
139150
[]
140151
end)
141152
end
142153

143-
# defp reconstruct_stream_lists(inserts, deletes) do
144-
# inserts
145-
# |> Enum.map(fn {stream_name, stream_inserts} ->
146-
# list = build_stream_list_from_inserts(stream_inserts)
147-
# deleted_ids = Map.get(deletes, stream_name, [])
148-
# # dbg(list)
149-
# # dbg(deleted_ids)
150-
151-
# cleaned =
152-
# Enum.reduce(deleted_ids, list, fn dom_id, acc ->
153-
# List.keydelete(acc, dom_id, 0)
154-
# end)
155-
156-
# {stream_name, cleaned}
157-
# end)
158-
# end
154+
defp flatten_stream_config(stream_entry) do
155+
configured = Map.get(stream_entry, :__configured__)
159156

160-
# defp build_stream_list_from_inserts(inserts) do
161-
# Enum.reduce(Enum.reverse(inserts), [], fn {dom_id, _, element, _, updated?}, acc ->
162-
# if updated? do
163-
# List.keyreplace(acc, dom_id, 0, {dom_id, element})
164-
# else
165-
# List.insert_at(acc, 0, {dom_id, element})
166-
# end
167-
# end)
168-
# end
169-
170-
# defp build_stream_state_map(list_map) do
171-
# list_map
172-
# |> Enum.map(fn {stream_name, list} ->
173-
# stream_state =
174-
# Enum.with_index(list, fn {dom_id, _val}, idx ->
175-
# {dom_id, idx}
176-
# end)
177-
178-
# {stream_name, stream_state}
179-
# end)
180-
# |> Map.new()
181-
# end
182-
183-
# defp build_diff_from_computed_lists(list_map) do
184-
# diff_map =
185-
# Enum.reduce(list_map, %{}, fn {stream_name, list}, acc ->
186-
# inserts =
187-
# list
188-
# |> Enum.with_index()
189-
# |> Enum.into(%{}, fn {{_dom_id, val}, index} ->
190-
# {index, val}
191-
# end)
192-
193-
# # dbg(inserts)
194-
195-
# list_diff = %Diff{type: :list, ins: inserts, del: %{}, diff: %{}}
196-
# Map.put(acc, stream_name, list_diff)
197-
# end)
198-
199-
# Map.put(@empty_diff, :diff, diff_map)
200-
# end
201-
202-
# defp compute_stream_diff_update(inserts, deletes, current_state) do
203-
# deleted_ids = MapSet.new(deletes)
204-
205-
# cleaned_state =
206-
# Enum.reject(current_state, fn {dom_id, _} -> dom_id in deleted_ids end)
207-
208-
# reindexed_state =
209-
# cleaned_state
210-
# |> Enum.with_index()
211-
# |> Enum.map(fn {{dom_id, _}, idx} -> {dom_id, idx} end)
212-
213-
# deleted_map =
214-
# deletes
215-
# |> Enum.map(fn dom_id ->
216-
# case List.keyfind(current_state, dom_id, 0) do
217-
# {^dom_id, idx} -> {idx, :deleted}
218-
# _ -> nil
219-
# end
220-
# end)
221-
# |> Enum.reject(&is_nil/1)
222-
# |> Enum.into(%{})
223-
224-
# {insert_ops, updated_del_map, updated_state} =
225-
# Enum.reduce(inserts, {[], %{}, reindexed_state}, fn
226-
# {dom_id, index, value, _, true}, {ins_acc, del_acc, state_acc} ->
227-
# {_, original_index} = List.keyfind(current_state, dom_id, 0)
228-
229-
# updated_del =
230-
# if original_index, do: Map.put(del_acc, original_index, :deleted), else: del_acc
231-
232-
# # tu cos nie dzila jeszcze i kolejnosc jeszce jak insertujemy pod ten sam domid bez update to tez update xd i
233-
# # insert at index jesli jest mniej elementow to na koniec(update)
234-
# updated_ins = List.insert_at(ins_acc, index, {dom_id, value})
235-
# new_state = List.keyreplace(state_acc, dom_id, 0, {dom_id, index})
236-
237-
# {updated_ins, updated_del, new_state}
238-
239-
# {dom_id, index, value, _, false}, {ins_acc, del_acc, state_acc} ->
240-
# {
241-
# List.insert_at(ins_acc, index, {dom_id, value}),
242-
# del_acc,
243-
# List.insert_at(state_acc, index, {dom_id, index})
244-
# }
245-
# end)
246-
247-
# # dbg(ins_map)
248-
249-
# final_state =
250-
# updated_state
251-
# |> Enum.with_index()
252-
# |> Enum.map(fn {{dom_id, _}, idx} -> {dom_id, idx} end)
253-
254-
# # dbg({insert_ops, final_state})
255-
256-
# ins_map =
257-
# insert_ops
258-
# |> Enum.map(fn {dom_id, val} ->
259-
# {Keyword.fetch!(final_state, dom_id), val}
260-
# end)
261-
# |> Map.new()
262-
263-
# # dbg(ins_map)
157+
Enum.flat_map(stream_entry, fn
158+
{stream_name, %Phoenix.LiveView.LiveStream{} = stream} ->
159+
name_config = Map.get(configured, stream_name)
160+
map_stream_entry_to_stream_config(stream, name_config)
264161

265-
# %{
266-
# ins: ins_map,
267-
# del: Map.merge(deleted_map, updated_del_map),
268-
# final_state: final_state
269-
# }
270-
# end
162+
_ ->
163+
[]
164+
end)
165+
end
271166
end

0 commit comments

Comments
 (0)