Add LiveStash for state recovery across reconnects

- Installed live_stash ~> 0.1 with browser memory adapter
- Map page: recovers selected_band and selected_time on reconnect
- Submit page: recovers active_tab (single/csv) on reconnect
- JS params initialized with initLiveStash wrapper
This commit is contained in:
Graham McIntire 2026-04-02 13:12:17 -05:00
parent 2f9d645c50
commit c1da0f20b1
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
5 changed files with 53 additions and 33 deletions

View file

@ -29,6 +29,7 @@ import {LiveSocket} from "phoenix_live_view"
import {hooks as colocatedHooks} from "phoenix-colocated/microwaveprop"
import topbar from "../vendor/topbar"
import initLiveStash from "../../deps/live_stash/assets/js/live-stash.js"
import {PropagationMap} from "./propagation_map_hook"
import {ContactMap} from "./contact_map_hook"
import {ContactsMap} from "./contacts_map_hook"
@ -53,7 +54,7 @@ const UtcClock = {
const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
const liveSocket = new LiveSocket("/live", Socket, {
longPollFallbackMs: 2500,
params: {_csrf_token: csrfToken},
params: initLiveStash({_csrf_token: csrfToken}),
hooks: {...colocatedHooks, PropagationMap, UtcClock, ContactMap, ContactsMap, ElevationProfile},
})

View file

@ -1,6 +1,7 @@
defmodule MicrowavepropWeb.MapLive do
@moduledoc false
use MicrowavepropWeb, :live_view
use LiveStash
alias Microwaveprop.Propagation
alias Microwaveprop.Propagation.BandConfig
@ -18,15 +19,20 @@ defmodule MicrowavepropWeb.MapLive do
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "propagation:updated")
end
case LiveStash.recover_state(socket) do
{:recovered, socket} ->
{:ok, socket}
_ ->
bands = BandConfig.all_bands()
valid_times = Propagation.available_valid_times(@default_band)
selected_time = closest_to_now(valid_times)
initial_scores = Propagation.scores_at(@default_band, selected_time, @initial_bounds)
if connected?(socket) do
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "propagation:updated")
end
{:ok,
assign(socket,
page_title: "Propagation Map",
@ -40,6 +46,7 @@ defmodule MicrowavepropWeb.MapLive do
antenna_height_ft: 33
)}
end
end
@impl true
def handle_event("select_band", %{"value" => band}, socket) do
@ -51,6 +58,7 @@ defmodule MicrowavepropWeb.MapLive do
socket =
socket
|> assign(selected_band: band, valid_times: valid_times, selected_time: selected_time)
|> LiveStash.stash_assigns([:selected_band, :selected_time])
|> push_event("update_scores", %{scores: scores})
|> push_event("update_band_info", %{band_info: band_info(band)})
|> push_timeline()

View file

@ -1,6 +1,7 @@
defmodule MicrowavepropWeb.SubmitLive do
@moduledoc false
use MicrowavepropWeb, :live_view
use LiveStash
alias Microwaveprop.Radio
alias Microwaveprop.Radio.Contact
@ -26,11 +27,17 @@ defmodule MicrowavepropWeb.SubmitLive do
@impl true
def mount(_params, _session, socket) do
socket = allow_upload(socket, :csv, accept: ~w(.csv), max_entries: 1, max_file_size: 10_000_000)
case LiveStash.recover_state(socket) do
{:recovered, socket} ->
{:ok, socket}
_ ->
changeset = Radio.change_contact(%Contact{})
{:ok,
socket
|> assign(
assign(socket,
page_title: "Submit Contact",
form: to_form(changeset),
band_options: @band_options,
@ -39,13 +46,14 @@ defmodule MicrowavepropWeb.SubmitLive do
active_tab: :single,
csv_result: nil,
csv_email: ""
)
|> allow_upload(:csv, accept: ~w(.csv), max_entries: 1, max_file_size: 10_000_000)}
)}
end
end
@impl true
def handle_event("switch_tab", %{"tab" => tab}, socket) do
{:noreply, assign(socket, active_tab: String.to_existing_atom(tab))}
socket = assign(socket, active_tab: String.to_existing_atom(tab))
{:noreply, LiveStash.stash_assigns(socket, [:active_tab])}
end
def handle_event("validate", %{"contact" => contact_params}, socket) do

View file

@ -50,6 +50,7 @@ defmodule Microwaveprop.MixProject do
{:phoenix_live_view, "~> 1.1.0"},
{:lazy_html, ">= 0.1.0", only: :test},
{:stream_data, "~> 1.0", only: :test},
{:live_stash, "~> 0.1"},
{:phoenix_live_dashboard, "~> 0.8.3"},
{:esbuild, "~> 0.10", runtime: Mix.env() == :dev},
{:tailwind, "~> 0.3", runtime: Mix.env() == :dev},

View file

@ -26,6 +26,7 @@
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"lazy_html": {:hex, :lazy_html, "0.1.10", "ffe42a0b4e70859cf21a33e12a251e0c76c1dff76391609bd56702a0ef5bc429", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9.0", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "50f67e5faa09d45a99c1ddf3fac004f051997877dc8974c5797bb5ccd8e27058"},
"live_stash": {:hex, :live_stash, "0.1.1", "d6a715708d0d281608de62a15ee8aca63427046daaa6333a235aa9e55f2cccb2", [:mix], [{:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:uniq, "~> 0.6", [hex: :uniq, repo: "hexpm", optional: false]}], "hexpm", "96341ebd35513f6e71b4e268ef4d413b1e21048a3d759cafe0cd8378ad8df908"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
"mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"},
@ -60,6 +61,7 @@
"tidewave": {:hex, :tidewave, "0.5.6", "91f35540b5599640443f1d3a1c6166bf506e202840261a6344e384e8813c1f64", [:mix], [{:circular_buffer, "~> 0.4 or ~> 1.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_reload, ">= 1.6.1", [hex: :phoenix_live_reload, repo: "hexpm", optional: true]}, {:plug, "~> 1.17", [hex: :plug, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "dc82d52b8b6ffc04680544b17cd340c7d4166bb0d63999eb960850526866b533"},
"tzdata": {:hex, :tzdata, "1.1.3", "b1cef7bb6de1de90d4ddc25d33892b32830f907e7fc2fccd1e7e22778ab7dfbc", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "d4ca85575a064d29d4e94253ee95912edfb165938743dbf002acdf0dcecb0c28"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"},
"uniq": {:hex, :uniq, "0.6.2", "51846518c037134c08bc5b773468007b155e543d53c8b39bafe95b0af487e406", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "95aa2a41ea331ef0a52d8ed12d3e730ef9af9dbc30f40646e6af334fbd7bc0fc"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
"websock_adapter": {:hex, :websock_adapter, "0.5.9", "43dc3ba6d89ef5dec5b1d0a39698436a1e856d000d84bf31a3149862b01a287f", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "5534d5c9adad3c18a0f58a9371220d75a803bf0b9a3d87e6fe072faaeed76a08"},
"xla": {:hex, :xla, "0.10.0", "41121e9f011456242d3a79b9289910ce43419be0b0e7ebe67cc1292c6b3f232f", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f57d91aea6e661b52bf12239316c598679e9170628122bbd941235f040122bc6"},