diff --git a/lib/microwaveprop/radio/contact.ex b/lib/microwaveprop/radio/contact.ex index f5b2f570..0ff612e7 100644 --- a/lib/microwaveprop/radio/contact.ex +++ b/lib/microwaveprop/radio/contact.ex @@ -39,13 +39,14 @@ defmodule Microwaveprop.Radio.Contact do field :user_submitted, :boolean, default: false field :submitter_email, :string + field :user_id, :binary_id field :flagged_invalid, :boolean, default: false timestamps(type: :utc_datetime) end @required_fields ~w(station1 station2 qso_timestamp mode band)a - @optional_fields ~w(grid1 grid2 pos1 pos2 distance_km)a + @optional_fields ~w(grid1 grid2 pos1 pos2 distance_km user_id)a def changeset(contact, attrs) do contact @@ -53,14 +54,16 @@ defmodule Microwaveprop.Radio.Contact do |> validate_required(@required_fields) end - @submission_fields ~w(station1 station2 qso_timestamp mode band grid1 grid2 submitter_email)a + @submission_fields ~w(station1 station2 qso_timestamp mode band grid1 grid2 submitter_email user_id)a + @submission_required ~w(station1 station2 qso_timestamp mode band grid1 grid2)a @allowed_modes ~w(CW SSB FM FT8 FT4) @allowed_bands Enum.map(~w(1296 2304 3456 5760 10000 24000 47000 68000 75000 122000 134000 241000), &Decimal.new/1) def submission_changeset(contact, attrs) do contact |> cast(attrs, @submission_fields) - |> validate_required(@submission_fields) + |> validate_required(@submission_required) + |> validate_user_or_email() |> sanitize_callsign(:station1) |> sanitize_callsign(:station2) |> sanitize_grid(:grid1) @@ -71,12 +74,33 @@ defmodule Microwaveprop.Radio.Contact do |> validate_grid_format(:grid2) |> validate_inclusion(:mode, @allowed_modes) |> validate_inclusion(:band, @allowed_bands) - |> validate_format(:submitter_email, ~r/^[^\s<>]+@[^\s<>]+\.[^\s<>]+$/) + |> validate_email_format() |> validate_length(:submitter_email, max: 254) |> validate_length(:station1, max: 20) |> validate_length(:station2, max: 20) end + # Either a user_id (logged-in submitter) or a submitter_email (anonymous) + # must be present so we know who submitted the contact. + defp validate_user_or_email(changeset) do + user_id = get_field(changeset, :user_id) + email = get_field(changeset, :submitter_email) + + if user_id || (email && email != "") do + changeset + else + add_error(changeset, :submitter_email, "can't be blank") + end + end + + defp validate_email_format(changeset) do + case get_field(changeset, :submitter_email) do + nil -> changeset + "" -> changeset + _email -> validate_format(changeset, :submitter_email, ~r/^[^\s<>]+@[^\s<>]+\.[^\s<>]+$/) + end + end + # Strip whitespace and upcase callsigns defp sanitize_callsign(changeset, field) do case get_change(changeset, field) do diff --git a/lib/microwaveprop_web/live/submit_live.ex b/lib/microwaveprop_web/live/submit_live.ex index a09ba88e..6d566046 100644 --- a/lib/microwaveprop_web/live/submit_live.ex +++ b/lib/microwaveprop_web/live/submit_live.ex @@ -66,6 +66,8 @@ defmodule MicrowavepropWeb.SubmitLive do if recently_submitted?(socket) do {:noreply, put_flash(socket, :error, "Please wait before submitting another contact.")} else + contact_params = merge_user_params(contact_params, socket) + case Radio.create_contact(contact_params) do {:ok, contact} -> ContactWeatherEnqueueWorker.enqueue_for_contact(contact) @@ -82,8 +84,14 @@ defmodule MicrowavepropWeb.SubmitLive do end end - def handle_event("upload_csv", %{"submitter_email" => email}, socket) do - if String.trim(email) == "" do + def handle_event("upload_csv", params, socket) do + email = + case current_user(socket.assigns) do + %{email: user_email} -> user_email + _ -> params |> Map.get("submitter_email", "") |> String.trim() + end + + if email == "" do {:noreply, put_flash(socket, :error, "Email is required for CSV upload")} else uploaded_contents = @@ -144,6 +152,18 @@ defmodule MicrowavepropWeb.SubmitLive do end end + defp merge_user_params(params, socket) do + case current_user(socket.assigns) do + %{id: user_id, email: user_email} -> + params + |> Map.put("user_id", user_id) + |> Map.put("submitter_email", user_email) + + _ -> + params + end + end + @impl true def render(assigns) do ~H""" @@ -156,10 +176,10 @@ defmodule MicrowavepropWeb.SubmitLive do

Every contact matters

- Our propagation model improves with real-world data. Each verified microwave contact + Our propagation model improves with real-world data. Each verified contact you submit is matched against atmospheric conditions at the time of your contact, - helping us calibrate predictions for all bands from 10 GHz to 241 GHz. The more contacts - we have, the better our forecasts get for everyone. + helping us calibrate predictions for all amateur bands from 902 MHz and up. The + more contacts we have, the better our forecasts get for everyone.

@@ -199,7 +219,12 @@ defmodule MicrowavepropWeb.SubmitLive do <%= if @active_tab == :single do %> - <.single_contact_form form={@form} band_options={@band_options} mode_options={@mode_options} /> + <.single_contact_form + form={@form} + band_options={@band_options} + mode_options={@mode_options} + current_user={current_user(assigns)} + /> <% else %> <%= cond do %> <% @csv_result -> %> @@ -207,13 +232,17 @@ defmodule MicrowavepropWeb.SubmitLive do <% @csv_preview -> %> <.csv_preview preview={@csv_preview} /> <% true -> %> - <.csv_upload_form uploads={@uploads} /> + <.csv_upload_form uploads={@uploads} current_user={current_user(assigns)} /> <% end %> <% end %> """ end + # In-template variant — takes an assigns map directly (not a socket). + defp current_user(%{current_scope: %{user: %{} = user}}), do: user + defp current_user(_), do: nil + defp single_contact_form(assigns) do ~H""" <.form for={@form} id="contact-form" phx-change="validate" phx-submit="save" class="space-y-4"> @@ -262,13 +291,21 @@ defmodule MicrowavepropWeb.SubmitLive do <.input field={@form[:qso_timestamp]} type="datetime-local" label="Timestamp (UTC)" required /> - <.input - field={@form[:submitter_email]} - type="email" - label="Your Email" - placeholder="you@example.com" - required - /> + <%= if @current_user do %> + + <% else %> + <.input + field={@form[:submitter_email]} + type="email" + label="Your Email" + placeholder="you@example.com" + required + /> + <% end %>
<.button phx-disable-with="Submitting..." class="btn btn-primary btn-lg w-full sm:w-auto"> @@ -311,18 +348,22 @@ defmodule MicrowavepropWeb.SubmitLive do
-
- -
+ <%= if @current_user do %> + + <% else %> +
+ +
+ <% end %>