- Public location can be hidden, approximate, or explicitly exact. Foreground tracking is
- optional. The current exact point is deleted when sharing stops, a match ends, or either
- participant blocks the other; derived movement and proximity evidence may remain.
+ Public location can be hidden, approximate, or explicitly exact. Live tracking is
+ optional. A browser shares only while its page is open; Android shows a persistent
+ foreground-service notification while sharing. The current exact point is deleted when
+ sharing stops, a match ends, or either participant blocks the other; derived movement and
+ proximity evidence may remain.
diff --git a/lib/who_need_help_web/live/request_live/show.ex b/lib/who_need_help_web/live/request_live/show.ex
index 58d0379..63c89d2 100644
--- a/lib/who_need_help_web/live/request_live/show.ex
+++ b/lib/who_need_help_web/live/request_live/show.ex
@@ -7,13 +7,29 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
def mount(%{"id" => id}, _session, socket) do
case Help.get_request(socket.assigns.current_scope, id) do
{:ok, request} ->
- socket = assign(socket, :subscribed_assignment_id, nil)
+ native_client =
+ connected?(socket) &&
+ get_connect_params(socket)["client_type"] == "android"
+
+ socket =
+ socket
+ |> assign(:subscribed_assignment_id, nil)
+ |> assign(:native_client, native_client)
if connected?(socket), do: Help.subscribe_request(id)
socket = maybe_subscribe_assignment(socket, request)
- {:ok, load(socket, request, false)}
+ tracking_active =
+ request.assignment &&
+ Tracking.active_session?(socket.assigns.current_scope, request.assignment)
+
+ socket =
+ socket
+ |> load(request, tracking_active)
+ |> maybe_start_native_tracking()
+
+ {:ok, socket}
{:error, :not_found} ->
{:ok,
@@ -50,7 +66,19 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
def handle_info({:tracking_stopped, user_id}, socket) do
positions = Map.delete(socket.assigns.positions, user_id)
- {:noreply, assign_positions(socket, positions)}
+
+ socket =
+ socket
+ |> assign_positions(positions)
+ |> then(fn socket ->
+ if user_id == socket.assigns.current_scope.user.id do
+ assign(socket, :tracking_active, false)
+ else
+ socket
+ end
+ end)
+
+ {:noreply, socket}
end
@impl true
@@ -66,8 +94,11 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
def handle_event("cancel-request", _, socket) do
case Help.cancel_request(socket.assigns.current_scope, socket.assigns.request.id) do
- {:ok, _request} -> {:noreply, put_flash(socket, :info, "Request cancelled.")}
- {:error, reason} -> {:noreply, put_flash(socket, :error, message(reason))}
+ {:ok, _request} ->
+ {:noreply, put_flash(socket, :info, "Request cancelled.")}
+
+ {:error, reason} ->
+ {:noreply, put_flash(socket, :error, message(reason))}
end
end
@@ -82,8 +113,11 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
def handle_event("verify-code", %{"handover" => %{"code" => code}}, socket) do
case Help.verify_handover(socket.assigns.current_scope, socket.assigns.assignment.id, code) do
- {:ok, _} -> {:noreply, put_flash(socket, :info, "Handover code verified.")}
- {:error, reason} -> {:noreply, put_flash(socket, :error, message(reason))}
+ {:ok, _} ->
+ {:noreply, put_flash(socket, :info, "Handover code verified.")}
+
+ {:error, reason} ->
+ {:noreply, put_flash(socket, :error, message(reason))}
end
end
@@ -106,8 +140,14 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
def handle_event("start-tracking", _, socket) do
case Tracking.start_session(socket.assigns.current_scope, socket.assigns.assignment) do
- {:ok, _} -> {:noreply, assign(socket, :tracking_active, true)}
- {:error, reason} -> {:noreply, put_flash(socket, :error, message(reason))}
+ {:ok, _} ->
+ {:noreply,
+ socket
+ |> assign(:tracking_active, true)
+ |> maybe_start_native_tracking()}
+
+ {:error, reason} ->
+ {:noreply, put_flash(socket, :error, message(reason))}
end
end
@@ -124,8 +164,14 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
def handle_event("stop-tracking", _, socket) do
case Tracking.stop_session(socket.assigns.current_scope, socket.assigns.assignment) do
- {:ok, _} -> {:noreply, assign(socket, :tracking_active, false)}
- {:error, reason} -> {:noreply, put_flash(socket, :error, message(reason))}
+ {:ok, _} ->
+ {:noreply,
+ socket
+ |> assign(:tracking_active, false)
+ |> maybe_stop_native_tracking()}
+
+ {:error, reason} ->
+ {:noreply, put_flash(socket, :error, message(reason))}
end
end
@@ -219,7 +265,12 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
@impl true
def terminate(_reason, socket) do
case socket.assigns do
- %{tracking_active: true, current_scope: scope, assignment: assignment}
+ %{
+ tracking_active: true,
+ native_client: false,
+ current_scope: scope,
+ assignment: assignment
+ }
when not is_nil(assignment) ->
Tracking.stop_session(scope, assignment)
@@ -230,6 +281,22 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
:ok
end
+ defp maybe_start_native_tracking(
+ %{assigns: %{native_client: true, tracking_active: true, assignment: assignment}} =
+ socket
+ )
+ when not is_nil(assignment) do
+ push_event(socket, "native-tracking-start", %{assignment_id: assignment.id})
+ end
+
+ defp maybe_start_native_tracking(socket), do: socket
+
+ defp maybe_stop_native_tracking(%{assigns: %{native_client: true}} = socket) do
+ push_event(socket, "native-tracking-stop", %{})
+ end
+
+ defp maybe_stop_native_tracking(socket), do: socket
+
defp transition(socket, fun, success) do
case fun.(socket.assigns.current_scope, socket.assigns.assignment.id) do
{:ok, _} -> {:noreply, put_flash(socket, :info, success)}
@@ -711,9 +778,11 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
:if={@participant && @assignment.status in [:accepted, :in_progress]}
class="rounded-3xl border border-base-300 p-5"
>
-
Foreground live location
+
Live location
- Works only while this page remains open. Raw coordinates are removed when sharing stops.
+ In a browser this works only while the page remains open. The Android app shows a
+ persistent notification and can continue after it is minimized. Raw coordinates are
+ removed when sharing stops.