fix(members): restore column visibility from URL on reload
All checks were successful
continuous-integration/drone/push Build is passing

Read 'fields' from URI when conn.params has no query (e.g. full page load).
When ?fields=... is present use URL-only selection so columns are not
merged with global settings. Fall back to session+global when URL has
only invalid field names.
This commit is contained in:
Moritz 2026-02-24 01:06:41 +01:00
parent e86c78a0dc
commit d41d13d122
Signed by: moritz
GPG key ID: 1020A035E5DD0824
2 changed files with 86 additions and 15 deletions

View file

@ -615,7 +615,9 @@ defmodule MvWeb.MemberLive.Index do
# -----------------------------------------------------------------
@impl true
def handle_params(params, _url, socket) do
def handle_params(params, url, socket) do
url = url || request_url_from_socket(socket)
params = merge_fields_param_from_uri(params, url)
prev_sig = build_signature(socket)
fields_in_url? =
@ -625,20 +627,7 @@ defmodule MvWeb.MemberLive.Index do
end
url_selection = FieldSelection.parse_from_url(params)
merged_selection =
FieldSelection.merge_sources(
url_selection,
socket.assigns.user_field_selection,
%{}
)
final_selection =
FieldVisibility.merge_with_global_settings(
merged_selection,
socket.assigns.settings,
socket.assigns.all_custom_fields
)
final_selection = compute_final_field_selection(fields_in_url?, url_selection, socket)
visible_member_fields =
final_selection
@ -828,6 +817,69 @@ defmodule MvWeb.MemberLive.Index do
add_boolean_filters(base_params, boolean_filters)
end
defp compute_final_field_selection(true, url_selection, socket) do
only_url =
FieldVisibility.selection_from_url_only(url_selection, socket.assigns.all_custom_fields)
visible = FieldVisibility.get_visible_member_fields(only_url)
if visible == [] do
# URL had only invalid field names; fall back to session + global.
compute_final_field_selection(false, url_selection, socket)
else
only_url
end
end
defp compute_final_field_selection(false, url_selection, socket) do
merged =
FieldSelection.merge_sources(
url_selection,
socket.assigns.user_field_selection,
%{}
)
FieldVisibility.merge_with_global_settings(
merged,
socket.assigns.settings,
socket.assigns.all_custom_fields
)
end
# On full page load conn.params has no query string; read "fields" from URI so column visibility is restored.
defp request_url_from_socket(socket) do
case socket.private[:connect_info] do
%Plug.Conn{} = conn -> Plug.Conn.request_url(conn)
_ -> nil
end
end
defp merge_fields_param_from_uri(params, nil), do: params
defp merge_fields_param_from_uri(params, %URI{query: query}) when is_binary(query) do
case URI.decode_query(query)["fields"] do
nil -> params
value -> Map.put(params, "fields", value)
end
end
defp merge_fields_param_from_uri(params, %URI{}), do: params
defp merge_fields_param_from_uri(params, url) when is_binary(url) do
case URI.parse(url).query do
nil ->
params
q ->
case URI.decode_query(q)["fields"] do
nil -> params
value -> Map.put(params, "fields", value)
end
end
end
defp merge_fields_param_from_uri(params, _), do: params
defp build_base_params(query, sort_field, sort_order) do
%{
"query" => query || "",

View file

@ -64,6 +64,25 @@ defmodule MvWeb.MemberLive.Index.FieldVisibility do
member_fields ++ custom_field_names
end
@doc """
Builds field selection from URL only: fields in `url_selection` are visible, all others false.
Use when `?fields=...` is in the URL so column visibility is not merged with global settings.
"""
@spec selection_from_url_only(%{String.t() => boolean()}, [struct()]) :: %{
String.t() => boolean()
}
def selection_from_url_only(url_selection, custom_fields) when is_map(url_selection) do
all_fields = get_all_available_fields(custom_fields)
Enum.reduce(all_fields, %{}, fn field, acc ->
field_string = field_to_string(field)
visible = Map.get(url_selection, field_string, false)
Map.put(acc, field_string, visible)
end)
end
def selection_from_url_only(_, _), do: %{}
@doc """
Merges user field selection with global settings.