Close terminal help interactions and clarify state
This commit is contained in:
parent
9acc52ef78
commit
f97b00bda8
|
|
@ -352,7 +352,7 @@ defmodule WhoNeedHelp.Help do
|
|||
is_nil(request) ->
|
||||
{:error, :not_found}
|
||||
|
||||
request.requester_id == user.id and request.status in [:open, :matched] ->
|
||||
request.requester_id == user.id and request.status in [:open, :matched, :in_progress] ->
|
||||
now = DateTime.utc_now(:second)
|
||||
|
||||
assignment =
|
||||
|
|
|
|||
|
|
@ -60,8 +60,15 @@ defmodule WhoNeedHelp.Messaging do
|
|||
request = current.request
|
||||
recipient_id = counterpart_id(user.id, current, request)
|
||||
|
||||
with true <- Help.participant?(scope, current),
|
||||
:ok <- Trust.lock_user_pair(user.id, recipient_id),
|
||||
cond do
|
||||
not Help.participant?(scope, current) ->
|
||||
{:error, :forbidden}
|
||||
|
||||
current.status not in [:accepted, :in_progress] ->
|
||||
{:error, :assignment_inactive}
|
||||
|
||||
true ->
|
||||
with :ok <- Trust.lock_user_pair(user.id, recipient_id),
|
||||
false <- Trust.blocked_between?(user.id, recipient_id),
|
||||
{:ok, message} <-
|
||||
%Message{assignment_id: current.id, sender_id: user.id}
|
||||
|
|
@ -77,9 +84,9 @@ defmodule WhoNeedHelp.Messaging do
|
|||
{:ok, message}
|
||||
else
|
||||
true -> {:error, :blocked}
|
||||
false -> {:error, :forbidden}
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
else
|
||||
{:error, :not_found}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -65,7 +65,13 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
def handle_info({event, request}, socket) when event in [:request_updated, :request_created] do
|
||||
case Help.get_request(socket.assigns.current_scope, request.id) do
|
||||
{:ok, request} ->
|
||||
socket = maybe_subscribe_assignment(socket, request)
|
||||
previous_status = socket.assigns.request.status
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> maybe_subscribe_assignment(request)
|
||||
|> maybe_put_status_transition_flash(previous_status, request.status)
|
||||
|
||||
{:noreply, sync_tracking_after_request_update(socket, request)}
|
||||
|
||||
{:error, :not_found} ->
|
||||
|
|
@ -369,11 +375,14 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
socket.assigns.other_user_id
|
||||
)
|
||||
|
||||
{:noreply,
|
||||
socket =
|
||||
socket
|
||||
|> assign(:blocked_by_current, false)
|
||||
|> assign(:interaction_blocked, interaction_blocked)
|
||||
|> put_flash(:info, gettext("User unblocked."))}
|
||||
|> restore_private_interaction(interaction_blocked)
|
||||
|> put_flash(:info, gettext("User unblocked."))
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, reason} ->
|
||||
{:noreply, put_flash(socket, :error, message(reason))}
|
||||
|
|
@ -436,6 +445,55 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
end
|
||||
end
|
||||
|
||||
defp maybe_put_status_transition_flash(socket, status, status), do: socket
|
||||
|
||||
defp maybe_put_status_transition_flash(socket, _previous, :matched) do
|
||||
put_flash(
|
||||
socket,
|
||||
:info,
|
||||
gettext("A volunteer accepted this request. Use private chat to coordinate safely.")
|
||||
)
|
||||
end
|
||||
|
||||
defp maybe_put_status_transition_flash(socket, _previous, :in_progress) do
|
||||
put_flash(socket, :info, gettext("Help is now in progress."))
|
||||
end
|
||||
|
||||
defp maybe_put_status_transition_flash(socket, _previous, :completed) do
|
||||
put_flash(socket, :info, gettext("Help is complete."))
|
||||
end
|
||||
|
||||
defp maybe_put_status_transition_flash(socket, _previous, :cancelled) do
|
||||
put_flash(socket, :info, gettext("This request was cancelled."))
|
||||
end
|
||||
|
||||
defp maybe_put_status_transition_flash(socket, _previous, _status), do: socket
|
||||
|
||||
defp restore_private_interaction(socket, true), do: socket
|
||||
|
||||
defp restore_private_interaction(%{assigns: %{assignment: nil}} = socket, false), do: socket
|
||||
|
||||
defp restore_private_interaction(socket, false) do
|
||||
assignment = socket.assigns.assignment
|
||||
|
||||
messages_page =
|
||||
Messaging.paginate_messages(socket.assigns.current_scope, assignment)
|
||||
|
||||
positions =
|
||||
if assignment.status in [:accepted, :in_progress],
|
||||
do: Tracking.list_current_positions(socket.assigns.current_scope, assignment),
|
||||
else: %{}
|
||||
|
||||
socket
|
||||
|> assign(:messages, messages_page.entries)
|
||||
|> assign(:messages_cursor, messages_page.next_cursor)
|
||||
|> assign(
|
||||
:message_window_limit,
|
||||
max(length(messages_page.entries), @initial_message_window)
|
||||
)
|
||||
|> assign_positions(positions)
|
||||
end
|
||||
|
||||
defp load(socket, request, tracking_active) do
|
||||
assignment = request.assignment
|
||||
participant = assignment && Help.participant?(socket.assigns.current_scope, assignment)
|
||||
|
|
@ -699,6 +757,12 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
defp status_label(:cancelled), do: gettext("Cancelled")
|
||||
defp status_label(value), do: value
|
||||
|
||||
defp current_user_confirmed?(scope, request, assignment) do
|
||||
if Help.requester?(scope, request),
|
||||
do: not is_nil(assignment.requester_confirmed_at),
|
||||
else: not is_nil(assignment.helper_confirmed_at)
|
||||
end
|
||||
|
||||
defp visibility_label(:approximate_public), do: gettext("Approximate publicly")
|
||||
defp visibility_label(:hidden), do: gettext("Hidden")
|
||||
defp visibility_label(:exact_for_active_match), do: gettext("Exact only for active match")
|
||||
|
|
@ -892,7 +956,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
</div>
|
||||
</div>
|
||||
<.form
|
||||
:if={!@interaction_blocked}
|
||||
:if={!@interaction_blocked && @assignment.status in [:accepted, :in_progress]}
|
||||
id="message-form"
|
||||
for={@message_form}
|
||||
phx-submit="send-message"
|
||||
|
|
@ -918,6 +982,14 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
{gettext("Send")}
|
||||
</.button>
|
||||
</.form>
|
||||
<p
|
||||
:if={!@interaction_blocked && @assignment.status in [:completed, :cancelled]}
|
||||
class="mt-4 rounded-2xl bg-base-200 p-4 text-sm text-base-content/65"
|
||||
>
|
||||
{if @assignment.status == :completed,
|
||||
do: gettext("This help request is complete. Chat history is read-only."),
|
||||
else: gettext("This help request was cancelled. Chat history is read-only.")}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section
|
||||
|
|
@ -1036,6 +1108,44 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-2 rounded-xl bg-white/10 p-3 text-xs">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<span>{gettext("Requester confirmed")}</span>
|
||||
<span class="badge badge-sm">
|
||||
{if @assignment.requester_confirmed_at,
|
||||
do: gettext("Yes"),
|
||||
else: gettext("Pending")}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<span>{gettext("Helper confirmed")}</span>
|
||||
<span class="badge badge-sm">
|
||||
{if @assignment.helper_confirmed_at,
|
||||
do: gettext("Yes"),
|
||||
else: gettext("Pending")}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<span>{gettext("Handover code")}</span>
|
||||
<span class="badge badge-sm">
|
||||
{if @assignment.handover_verified_at,
|
||||
do: gettext("Verified"),
|
||||
else: gettext("Pending")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:if={@assignment.status == :completed}
|
||||
class="alert alert-success text-sm text-base-content"
|
||||
>
|
||||
{gettext("Help completed and verified by both participants.")}
|
||||
</div>
|
||||
<div
|
||||
:if={@assignment.status == :cancelled}
|
||||
class="alert alert-warning text-sm text-base-content"
|
||||
>
|
||||
{gettext("This match was cancelled. No further handover action is required.")}
|
||||
</div>
|
||||
<div
|
||||
:if={@assignment.helper.social_identities != []}
|
||||
class="flex flex-wrap gap-2 rounded-xl bg-white/10 p-3"
|
||||
|
|
@ -1060,12 +1170,24 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
{gettext("Start helping")}
|
||||
</button>
|
||||
<button
|
||||
:if={@assignment.status in [:accepted, :in_progress]}
|
||||
:if={
|
||||
@assignment.status in [:accepted, :in_progress] &&
|
||||
!current_user_confirmed?(@current_scope, @request, @assignment)
|
||||
}
|
||||
phx-click="confirm"
|
||||
class="btn btn-outline w-full"
|
||||
>
|
||||
{gettext("Confirm my part is complete")}
|
||||
</button>
|
||||
<p
|
||||
:if={
|
||||
@assignment.status in [:accepted, :in_progress] &&
|
||||
current_user_confirmed?(@current_scope, @request, @assignment)
|
||||
}
|
||||
class="rounded-xl bg-success/20 p-3 text-sm"
|
||||
>
|
||||
{gettext("Your completion confirmation is saved.")}
|
||||
</p>
|
||||
<button
|
||||
:if={
|
||||
Help.helper?(@current_scope, @assignment) &&
|
||||
|
|
@ -1088,7 +1210,11 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
</button>
|
||||
|
||||
<div
|
||||
:if={Help.requester?(@current_scope, @request)}
|
||||
:if={
|
||||
Help.requester?(@current_scope, @request) &&
|
||||
@assignment.status in [:accepted, :in_progress] &&
|
||||
is_nil(@assignment.handover_verified_at)
|
||||
}
|
||||
class="rounded-xl bg-white/10 p-4 text-center"
|
||||
>
|
||||
<div class="text-xs opacity-65">
|
||||
|
|
@ -1115,6 +1241,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
<.form
|
||||
:if={
|
||||
Help.helper?(@current_scope, @assignment) &&
|
||||
@assignment.status in [:accepted, :in_progress] &&
|
||||
is_nil(@assignment.handover_verified_at)
|
||||
}
|
||||
id="handover-form"
|
||||
|
|
|
|||
|
|
@ -167,6 +167,41 @@ defmodule WhoNeedHelp.MutualAidFlowTest do
|
|||
Messaging.send_message(outsider_scope, assignment, %{"body" => "not allowed"})
|
||||
end
|
||||
|
||||
test "cancelling in-progress help closes interaction and deletes live positions", context do
|
||||
{:ok, request} = Help.create_request(context.requester_scope, context.request_attrs)
|
||||
{:ok, assignment} = Help.accept_request(context.helper_scope, request.id)
|
||||
{:ok, assignment} = Help.start_assignment(context.helper_scope, assignment.id)
|
||||
|
||||
{:ok, message} =
|
||||
Messaging.send_message(context.helper_scope, assignment, %{"body" => "I have started."})
|
||||
|
||||
{:ok, tracking_session} = Tracking.start_session(context.helper_scope, assignment)
|
||||
|
||||
{:ok, _position} =
|
||||
Tracking.update_position(context.helper_scope, assignment, %{
|
||||
"latitude" => 50.45,
|
||||
"longitude" => 30.52,
|
||||
"accuracy_meters" => 8.0
|
||||
})
|
||||
|
||||
assert {:ok, cancelled_request} =
|
||||
Help.cancel_request(context.requester_scope, request.id)
|
||||
|
||||
assert cancelled_request.status == :cancelled
|
||||
assert cancelled_request.assignment.status == :cancelled
|
||||
refute Repo.get_by(Position, tracking_session_id: tracking_session.id)
|
||||
|
||||
assert {:error, :assignment_inactive} =
|
||||
Messaging.send_message(context.helper_scope, assignment, %{
|
||||
"body" => "This must not be stored."
|
||||
})
|
||||
|
||||
assert [%{id: message_id, body: "I have started."}] =
|
||||
Messaging.list_messages(context.requester_scope, assignment)
|
||||
|
||||
assert message_id == message.id
|
||||
end
|
||||
|
||||
test "participants can mark only counterpart messages as read", context do
|
||||
outsider_scope = user_fixture(display_name: "Outsider") |> user_scope_fixture()
|
||||
{:ok, request} = Help.create_request(context.requester_scope, context.request_attrs)
|
||||
|
|
|
|||
|
|
@ -625,6 +625,89 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
|||
refute has_element?(view, "#message-form")
|
||||
end
|
||||
|
||||
test "unblocking restores active match history without a page reload", %{
|
||||
conn: requester_conn,
|
||||
scope: requester_scope
|
||||
} do
|
||||
category = Catalog.seed_defaults()
|
||||
helper = user_fixture(display_name: "Restored helper")
|
||||
|
||||
{:ok, request} = Help.create_request(requester_scope, request_attrs(category))
|
||||
{:ok, assignment} = Help.accept_request(user_scope_fixture(helper), request.id)
|
||||
|
||||
{:ok, _message} =
|
||||
Messaging.send_message(user_scope_fixture(helper), assignment, %{
|
||||
"body" => "Restore this message after unblocking."
|
||||
})
|
||||
|
||||
{:ok, view, html} = live(requester_conn, ~p"/requests/#{request.id}")
|
||||
assert html =~ "Restore this message after unblocking."
|
||||
assert has_element?(view, "#message-form")
|
||||
|
||||
view
|
||||
|> element("button[phx-click='block-user']")
|
||||
|> render_click()
|
||||
|
||||
blocked_html = render(view)
|
||||
assert blocked_html =~ "This interaction is blocked."
|
||||
refute blocked_html =~ "Restore this message after unblocking."
|
||||
refute has_element?(view, "#message-form")
|
||||
|
||||
view
|
||||
|> element("button[phx-click='unblock-user']")
|
||||
|> render_click()
|
||||
|
||||
restored_html = render(view)
|
||||
assert restored_html =~ "Restore this message after unblocking."
|
||||
assert has_element?(view, "#message-form")
|
||||
end
|
||||
|
||||
test "cancelled in-progress help keeps history read-only and hides handover actions" do
|
||||
category = Catalog.seed_defaults()
|
||||
requester = user_fixture(display_name: "Cancelling requester")
|
||||
helper = user_fixture(display_name: "Cancelled helper")
|
||||
requester_scope = user_scope_fixture(requester)
|
||||
helper_scope = user_scope_fixture(helper)
|
||||
|
||||
{:ok, request} = Help.create_request(requester_scope, request_attrs(category))
|
||||
{:ok, assignment} = Help.accept_request(helper_scope, request.id)
|
||||
|
||||
{:ok, _message} =
|
||||
Messaging.send_message(helper_scope, assignment, %{"body" => "Visible history."})
|
||||
|
||||
{:ok, requester_view, _html} =
|
||||
build_conn() |> log_in_user(requester) |> live(~p"/requests/#{request.id}")
|
||||
|
||||
{:ok, helper_view, _html} =
|
||||
build_conn() |> log_in_user(helper) |> live(~p"/requests/#{request.id}")
|
||||
|
||||
helper_view
|
||||
|> element("button[phx-click='start']")
|
||||
|> render_click()
|
||||
|
||||
assert render(helper_view) =~ "Help is now in progress."
|
||||
|
||||
requester_view
|
||||
|> element("button[phx-click='cancel-request']")
|
||||
|> render_click()
|
||||
|
||||
for view <- [requester_view, helper_view] do
|
||||
html = render(view)
|
||||
assert html =~ "This match was cancelled"
|
||||
assert html =~ "This request was cancelled."
|
||||
refute html =~ "Help is now in progress."
|
||||
assert html =~ "Chat history is read-only"
|
||||
assert html =~ "Visible history."
|
||||
refute has_element?(view, "#message-form")
|
||||
refute has_element?(view, "#handover-form")
|
||||
refute has_element?(view, "#copy-handover-code")
|
||||
refute has_element?(view, "button[phx-click='confirm']")
|
||||
end
|
||||
|
||||
stop_live_view(requester_view)
|
||||
stop_live_view(helper_view)
|
||||
end
|
||||
|
||||
test "terminal request updates stop Android foreground tracking", _context do
|
||||
category = Catalog.seed_defaults()
|
||||
requester = user_fixture(display_name: "Tracking requester")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user