diff --git a/lib/microwaveprop_web/live/rover_locations_live.ex b/lib/microwaveprop_web/live/rover_locations_live.ex index 45c6f139..828d4fb2 100644 --- a/lib/microwaveprop_web/live/rover_locations_live.ex +++ b/lib/microwaveprop_web/live/rover_locations_live.ex @@ -56,6 +56,7 @@ defmodule MicrowavepropWeb.RoverLocationsLive do page_title: "Rover Locations", form: nil, editing_id: nil, + grid_input: "", status_filter: nil ) |> assign(:data_provider, {__MODULE__, :visible_query_provider, [nil]})} @@ -75,7 +76,7 @@ defmodule MicrowavepropWeb.RoverLocationsLive do def handle_event("new", _params, socket) do if user = current_user(socket) do - {:noreply, assign(socket, form: blank_form(user), editing_id: nil)} + {:noreply, assign(socket, form: blank_form(user), editing_id: nil, grid_input: "")} else {:noreply, put_flash(socket, :error, "Sign in to add a location.")} end @@ -87,7 +88,8 @@ defmodule MicrowavepropWeb.RoverLocationsLive do case Repo.get(Location, id) do %Location{user_id: ^user_id} = loc -> cs = Location.changeset(loc, %{}) - {:noreply, assign(socket, form: to_form(cs), editing_id: id)} + grid = Maidenhead.from_latlon(loc.lat, loc.lon, 6) + {:noreply, assign(socket, form: to_form(cs), editing_id: id, grid_input: grid)} _ -> {:noreply, put_flash(socket, :error, "You can only edit your own locations.")} @@ -99,13 +101,16 @@ defmodule MicrowavepropWeb.RoverLocationsLive do end def handle_event("cancel", _, socket) do - {:noreply, assign(socket, form: nil, editing_id: nil)} + {:noreply, assign(socket, form: nil, editing_id: nil, grid_input: "")} end - def handle_event("validate", %{"location" => params}, socket) do + def handle_event("validate", %{"location" => params} = event_params, socket) do + target = event_params["_target"] + {params, grid_input} = sync_grid_and_latlon(params, target, socket.assigns.grid_input) + base = if socket.assigns.editing_id, do: location_for_edit(socket), else: %Location{} cs = base |> Location.changeset(params) |> Map.put(:action, :validate) - {:noreply, assign(socket, form: to_form(cs))} + {:noreply, assign(socket, form: to_form(cs), grid_input: grid_input)} end def handle_event("save", %{"location" => params}, socket) do @@ -201,6 +206,44 @@ defmodule MicrowavepropWeb.RoverLocationsLive do defp parse_status(value) when value in @valid_status_filters, do: value defp parse_status(_), do: nil + # Two-way grid <-> lat/lon sync. `_target` from the phx-change event + # tells us which field the user actually edited; we mirror the change + # into the other side. Unparseable input leaves the other side alone. + defp sync_grid_and_latlon(params, ["location", "grid"], _old_grid) do + raw = params |> Map.get("grid", "") |> to_string() |> String.trim() + + case Maidenhead.to_latlon(raw) do + {:ok, {lat, lon}} -> + params = + params + |> Map.put("lat", format_coord(lat)) + |> Map.put("lon", format_coord(lon)) + + {params, raw} + + :error -> + {params, raw} + end + end + + defp sync_grid_and_latlon(params, ["location", field], old_grid) when field in ["lat", "lon"] do + with {lat, ""} <- params |> Map.get("lat", "") |> to_string() |> String.trim() |> Float.parse(), + {lon, ""} <- params |> Map.get("lon", "") |> to_string() |> String.trim() |> Float.parse(), + lat when lat >= -90.0 and lat <= 90.0 <- lat, + lon when lon >= -180.0 and lon <= 180.0 <- lon do + grid = Maidenhead.from_latlon(lat, lon, 6) + {Map.put(params, "grid", grid), grid} + else + _ -> {params, old_grid} + end + end + + defp sync_grid_and_latlon(params, _target, old_grid), do: {params, old_grid} + + defp format_coord(value) do + value |> Float.round(6) |> Float.to_string() + end + defp current_user(%Phoenix.LiveView.Socket{assigns: assigns}), do: scope_user(assigns) defp current_user(assigns) when is_map(assigns), do: scope_user(assigns) @@ -325,7 +368,22 @@ defmodule MicrowavepropWeb.RoverLocationsLive do {if @editing_id, do: "Edit location", else: "New location"} <.form for={@form} phx-change="validate" phx-submit="save" id="location-form"> -