diff --git a/assets/js/hooks.js b/assets/js/hooks.js index 04860db..cfffa77 100644 --- a/assets/js/hooks.js +++ b/assets/js/hooks.js @@ -337,7 +337,6 @@ const createAidMap = element => { element, map: null, active: true, - pendingViewportAction: null, resizeFrame: null, resizeObserver: null, renderedSize: null, @@ -948,11 +947,19 @@ const createAidMap = element => { const viewport = mapViewport(state.map) if (!viewport) return - state.pendingViewportAction = { - viewport, - clusterExpansion: state.clusterExpansionPending - } + const clusterExpansion = state.clusterExpansionPending state.clusterExpansionPending = false + + // `moveend` already means that the camera transition has finished. Do + // not wait for MapLibre's `idle` event here: `idle` also waits for every + // requested map tile and fade animation, so a slow tile server can delay + // discovery refreshes for seconds even though the user stopped moving + // the map earlier. + if (clusterExpansion) { + state.onClusterExpand?.(viewport) + } else { + state.onViewportChange?.(viewport) + } } state.onIdle = () => { @@ -964,15 +971,6 @@ const createAidMap = element => { } state.syncVisibleAreas() - - const action = state.pendingViewportAction - state.pendingViewportAction = null - - if (action?.clusterExpansion) { - state.onClusterExpand?.(action.viewport) - } else if (action) { - state.onViewportChange?.(action.viewport) - } } state.onRequestHighlight = event => state.highlightRequest(event.detail?.id) diff --git a/lib/who_need_help/activities.ex b/lib/who_need_help/activities.ex index 3389e7a..0c36158 100644 --- a/lib/who_need_help/activities.ex +++ b/lib/who_need_help/activities.ex @@ -62,8 +62,7 @@ defmodule WhoNeedHelp.Activities do def map_discovery_items(%Scope{user: user}, filters, %DiscoveryViewport{} = viewport) do level = DiscoveryCluster.level_for_zoom(viewport.zoom) - cell_size = DiscoveryCluster.cell_size_meters(level) - world_half = DiscoveryCluster.world_half_meters() + cell_count = Integer.pow(2, level) * 1.0 points = user @@ -75,93 +74,44 @@ defmodule WhoNeedHelp.Activities do ) |> select([activity], %{ id: activity.id, - title: activity.title, - location_label: activity.location_label, - public_location: - fragment( - """ - CASE - WHEN ? @> '{"scale_fixture": true}'::jsonb THEN ? - ELSE - ST_SetSRID( - ST_MakePoint( - round(ST_X(?)::numeric, 2)::double precision, - round(ST_Y(?)::numeric, 2)::double precision - ), - 4326 - ) - END - """, - activity.structured_data, - activity.location, - activity.location, - activity.location - ) + public_longitude: activity.public_longitude, + public_latitude: activity.public_latitude }) points |> subquery() - |> group_by([point], [ - fragment( - "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) - ), - fragment( - "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) - ) - ]) + |> group_by([_point], [selected_as(:cell_x), selected_as(:cell_y)]) |> select([point], %{ count: count(point.id), - activity_id: fragment("CASE WHEN count(*) = 1 THEN min(?::text) END", point.id), - title: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.title), - location_label: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.location_label), - longitude: fragment("avg(ST_X(?))", point.public_location), - latitude: fragment("avg(ST_Y(?))", point.public_location), - west: fragment("min(ST_X(?))", point.public_location), - south: fragment("min(ST_Y(?))", point.public_location), - east: fragment("max(ST_X(?))", point.public_location), - north: fragment("max(ST_Y(?))", point.public_location), - west_m: fragment("min(ST_X(ST_Transform(?, 3857)))", point.public_location), - south_m: fragment("min(ST_Y(ST_Transform(?, 3857)))", point.public_location), - east_m: fragment("max(ST_X(ST_Transform(?, 3857)))", point.public_location), - north_m: fragment("max(ST_Y(ST_Transform(?, 3857)))", point.public_location), + activity_id: fragment("min(?::text)", point.id), + longitude: min(point.public_longitude), + latitude: min(point.public_latitude), + west: min(point.public_longitude), + south: min(point.public_latitude), + east: max(point.public_longitude), + north: max(point.public_latitude), cell_x: - fragment( - "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) + selected_as( + fragment( + "floor(((? + 180.0) / 360.0) * ?)::bigint", + point.public_longitude, + constant(^cell_count) + ), + :cell_x ), cell_y: - fragment( - "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) + selected_as( + fragment( + "floor(((ln(tan(pi() / 4.0 + radians(least(85.05112878, greatest(-85.05112878, ?))) / 2.0)) / pi() + 1.0) / 2.0) * ?)::bigint", + point.public_latitude, + constant(^cell_count) + ), + :cell_y ) }) - |> order_by([point], - asc: - fragment( - "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) - ), - asc: - fragment( - "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) - ) - ) + |> order_by([_point], asc: selected_as(:cell_x), asc: selected_as(:cell_y)) |> Repo.all() + |> hydrate_activity_map_singletons() |> Enum.map(&map_discovery_item(&1, level)) end @@ -894,28 +844,15 @@ defmodule WhoNeedHelp.Activities do ^condition or fragment( """ - ST_Intersects( - CASE - WHEN ? @> '{"scale_fixture": true}'::jsonb THEN ? - ELSE - ST_SetSRID( - ST_MakePoint( - round(ST_X(?)::numeric, 2)::double precision, - round(ST_Y(?)::numeric, 2)::double precision - ), - 4326 - ) - END, - ST_MakeEnvelope(?, ?, ?, ?, 4326) - ) + ? >= ? AND ? <= ? AND ? >= ? AND ? <= ? """, - activity.structured_data, - activity.location, - activity.location, - activity.location, + activity.public_longitude, ^west, - ^south, + activity.public_longitude, ^east, + activity.public_latitude, + ^south, + activity.public_latitude, ^north ) ) @@ -1022,12 +959,12 @@ defmodule WhoNeedHelp.Activities do hierarchy_level: level, point_count: row.count, expansion_zoom: - DiscoveryCluster.expansion_zoom( + DiscoveryCluster.expansion_zoom_for_bounds( level, - row.west_m, - row.south_m, - row.east_m, - row.north_m + row.west, + row.south, + row.east, + row.north ), count: row.count, latitude: latitude, @@ -1041,6 +978,36 @@ defmodule WhoNeedHelp.Activities do } end + defp hydrate_activity_map_singletons(rows) do + singleton_ids = + rows + |> Enum.filter(&(&1.count == 1)) + |> Enum.map(& &1.activity_id) + + details = activity_map_singleton_details(singleton_ids) + + Enum.map(rows, fn + %{count: 1, activity_id: activity_id} = row -> + Map.merge(row, Map.fetch!(details, activity_id)) + + row -> + row + end) + end + + defp activity_map_singleton_details([]), do: %{} + + defp activity_map_singleton_details(singleton_ids) do + Activity + |> where([activity], activity.id in ^singleton_ids) + |> select([activity], { + activity.id, + %{title: activity.title, location_label: activity.location_label} + }) + |> Repo.all() + |> Map.new(fn {id, detail} -> {to_string(id), detail} end) + end + defp escape_like(value) do value |> String.replace("\\", "\\\\") diff --git a/lib/who_need_help/activities/activity.ex b/lib/who_need_help/activities/activity.ex index 2adf712..935ef57 100644 --- a/lib/who_need_help/activities/activity.ex +++ b/lib/who_need_help/activities/activity.ex @@ -11,6 +11,8 @@ defmodule WhoNeedHelp.Activities.Activity do field :structured_data, :map, default: %{} field :location_label, :string field :location, Geo.PostGIS.Geometry + field :public_longitude, :float + field :public_latitude, :float field :status, Ecto.Enum, values: [:open, :completed, :cancelled], default: :open field :location_visibility, Ecto.Enum, diff --git a/lib/who_need_help/help.ex b/lib/who_need_help/help.ex index 7d47871..ce5f14a 100644 --- a/lib/who_need_help/help.ex +++ b/lib/who_need_help/help.ex @@ -73,8 +73,7 @@ defmodule WhoNeedHelp.Help do def map_discovery_items(%Scope{user: user}, filters, %DiscoveryViewport{} = viewport) do level = DiscoveryCluster.level_for_zoom(viewport.zoom) - cell_size = DiscoveryCluster.cell_size_meters(level) - world_half = DiscoveryCluster.world_half_meters() + cell_count = Integer.pow(2, level) * 1.0 points = user @@ -86,106 +85,46 @@ defmodule WhoNeedHelp.Help do |> filter_request_map_cluster_viewport(viewport, level) |> select([request], %{ id: request.id, - title: request.title, - location_label: request.location_label, - location_visibility: request.location_visibility, - radius_meters: request.location_radius_meters, urgency: request.urgency, - public_location: - fragment( - """ - CASE - WHEN ? @> '{"scale_fixture": true}'::jsonb THEN ? - WHEN ? = 'exact_for_active_match' THEN - ST_SetSRID( - ST_MakePoint( - round(ST_X(?)::numeric, 2)::double precision, - round(ST_Y(?)::numeric, 2)::double precision - ), - 4326 - ) - ELSE ? - END - """, - request.structured_data, - request.location, - request.location_visibility, - request.location, - request.location, - request.location - ) + public_longitude: request.public_longitude, + public_latitude: request.public_latitude }) points |> subquery() - |> group_by([point], [ - fragment( - "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) - ), - fragment( - "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) - ) - ]) + |> group_by([_point], [selected_as(:cell_x), selected_as(:cell_y)]) |> select([point], %{ count: count(point.id), urgent_count: fragment("count(*) FILTER (WHERE ? = 'now')::bigint", point.urgency), - request_id: fragment("CASE WHEN count(*) = 1 THEN min(?::text) END", point.id), - title: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.title), - location_label: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.location_label), - location_visibility: - fragment( - "CASE WHEN count(*) = 1 THEN min(?::text) END", - point.location_visibility - ), - radius_meters: fragment("CASE WHEN count(*) = 1 THEN max(?) END", point.radius_meters), - longitude: fragment("avg(ST_X(?))", point.public_location), - latitude: fragment("avg(ST_Y(?))", point.public_location), - west: fragment("min(ST_X(?))", point.public_location), - south: fragment("min(ST_Y(?))", point.public_location), - east: fragment("max(ST_X(?))", point.public_location), - north: fragment("max(ST_Y(?))", point.public_location), - west_m: fragment("min(ST_X(ST_Transform(?, 3857)))", point.public_location), - south_m: fragment("min(ST_Y(ST_Transform(?, 3857)))", point.public_location), - east_m: fragment("max(ST_X(ST_Transform(?, 3857)))", point.public_location), - north_m: fragment("max(ST_Y(ST_Transform(?, 3857)))", point.public_location), + request_id: fragment("min(?::text)", point.id), + longitude: min(point.public_longitude), + latitude: min(point.public_latitude), + west: min(point.public_longitude), + south: min(point.public_latitude), + east: max(point.public_longitude), + north: max(point.public_latitude), cell_x: - fragment( - "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) + selected_as( + fragment( + "floor(((? + 180.0) / 360.0) * ?)::bigint", + point.public_longitude, + constant(^cell_count) + ), + :cell_x ), cell_y: - fragment( - "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) + selected_as( + fragment( + "floor(((ln(tan(pi() / 4.0 + radians(least(85.05112878, greatest(-85.05112878, ?))) / 2.0)) / pi() + 1.0) / 2.0) * ?)::bigint", + point.public_latitude, + constant(^cell_count) + ), + :cell_y ) }) - |> order_by([point], - asc: - fragment( - "floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) - ), - asc: - fragment( - "floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint", - point.public_location, - constant(^world_half), - constant(^cell_size) - ) - ) + |> order_by([_point], asc: selected_as(:cell_x), asc: selected_as(:cell_y)) |> Repo.all() + |> hydrate_request_map_singletons() |> Enum.map(&map_discovery_item(&1, level)) end @@ -227,21 +166,7 @@ defmodule WhoNeedHelp.Help do ( ? && ST_MakeEnvelope(?, ?, ?, ?, 4326) AND - ST_Intersects( - CASE - WHEN ? @> '{"scale_fixture": true}'::jsonb THEN ? - WHEN ? = 'exact_for_active_match' THEN - ST_SetSRID( - ST_MakePoint( - round(ST_X(?)::numeric, 2)::double precision, - round(ST_Y(?)::numeric, 2)::double precision - ), - 4326 - ) - ELSE ? - END, - ST_MakeEnvelope(?, ?, ?, ?, 4326) - ) + ? >= ? AND ? <= ? AND ? >= ? AND ? <= ? ) """, request.location, @@ -249,15 +174,13 @@ defmodule WhoNeedHelp.Help do ^(south - 0.005), ^(east + 0.005), ^(north + 0.005), - request.structured_data, - request.location, - request.location_visibility, - request.location, - request.location, - request.location, + request.public_longitude, ^west, - ^south, + request.public_longitude, ^east, + request.public_latitude, + ^south, + request.public_latitude, ^north ) ) @@ -1273,7 +1196,7 @@ defmodule WhoNeedHelp.Help do location: row.location_label, latitude: row.latitude, longitude: row.longitude, - exact: row.location_visibility == "exact_public", + exact: row.location_visibility in ["exact_public", :exact_public], radius_meters: row.radius_meters } end @@ -1290,12 +1213,12 @@ defmodule WhoNeedHelp.Help do hierarchy_level: level, point_count: row.count, expansion_zoom: - DiscoveryCluster.expansion_zoom( + DiscoveryCluster.expansion_zoom_for_bounds( level, - row.west_m, - row.south_m, - row.east_m, - row.north_m + row.west, + row.south, + row.east, + row.north ), count: row.count, urgent_count: row.urgent_count, @@ -1310,6 +1233,41 @@ defmodule WhoNeedHelp.Help do } end + defp hydrate_request_map_singletons(rows) do + singleton_ids = + rows + |> Enum.filter(&(&1.count == 1)) + |> Enum.map(& &1.request_id) + + details = request_map_singleton_details(singleton_ids) + + Enum.map(rows, fn + %{count: 1, request_id: request_id} = row -> + Map.merge(row, Map.fetch!(details, request_id)) + + row -> + row + end) + end + + defp request_map_singleton_details([]), do: %{} + + defp request_map_singleton_details(singleton_ids) do + HelpRequest + |> where([request], request.id in ^singleton_ids) + |> select([request], { + request.id, + %{ + title: request.title, + location_label: request.location_label, + location_visibility: request.location_visibility, + radius_meters: request.location_radius_meters + } + }) + |> Repo.all() + |> Map.new(fn {id, detail} -> {to_string(id), detail} end) + end + defp normalize_urgency(value) when value in [:now, "now"], do: {:ok, :now} defp normalize_urgency(value) when value in [:today, "today"], do: {:ok, :today} defp normalize_urgency(value) when value in [:scheduled, "scheduled"], do: {:ok, :scheduled} diff --git a/lib/who_need_help/help/discovery_cluster.ex b/lib/who_need_help/help/discovery_cluster.ex index 9a1ff62..586f494 100644 --- a/lib/who_need_help/help/discovery_cluster.ex +++ b/lib/who_need_help/help/discovery_cluster.ex @@ -124,6 +124,27 @@ defmodule WhoNeedHelp.Help.DiscoveryCluster do |> clamp(0, @maximum_map_zoom) end + @doc """ + Returns the first expansion zoom for member bounds expressed as WGS84 + longitude and latitude values. + + Discovery aggregation already calculates geographic bounds. Projecting the + handful of returned clusters in Elixir avoids repeating `ST_Transform` for + every matching database row while preserving the same Web Mercator + hierarchy. + """ + def expansion_zoom_for_bounds(level, west, south, east, north) + when is_integer(level) and is_number(west) and is_number(south) and is_number(east) and + is_number(north) do + expansion_zoom( + level, + longitude_to_mercator_x(west), + latitude_to_mercator_y(south), + longitude_to_mercator_x(east), + latitude_to_mercator_y(north) + ) + end + defp cell_index(coordinate, level) do coordinate |> Kernel.+(@world_half_meters) diff --git a/lib/who_need_help/help/help_request.ex b/lib/who_need_help/help/help_request.ex index c233ae3..e5a69a7 100644 --- a/lib/who_need_help/help/help_request.ex +++ b/lib/who_need_help/help/help_request.ex @@ -12,6 +12,8 @@ defmodule WhoNeedHelp.Help.HelpRequest do field :structured_data, :map, default: %{} field :location_label, :string field :location, Geo.PostGIS.Geometry + field :public_longitude, :float + field :public_latitude, :float field :location_radius_meters, :integer, default: 1_000 field :status, Ecto.Enum, diff --git a/lib/who_need_help_web/live/activity_live/index.ex b/lib/who_need_help_web/live/activity_live/index.ex index 4813637..242460c 100644 --- a/lib/who_need_help_web/live/activity_live/index.ex +++ b/lib/who_need_help_web/live/activity_live/index.ex @@ -24,7 +24,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do |> assign(:discovery_mode, "nearby") |> assign(:view_mode, "split") |> assign(:view_explicit, false) - |> assign(:map_update_mode, "manual") + |> assign(:map_update_mode, "auto") |> assign(:viewport, nil) |> assign(:pending_viewport, nil) |> assign(:activities, []) @@ -402,7 +402,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do defp normalize_view_mode(_view_mode), do: "split" defp normalize_map_update_mode(mode) when mode in @map_update_modes, do: mode - defp normalize_map_update_mode(_mode), do: "manual" + defp normalize_map_update_mode(_mode), do: "auto" defp activity_path(socket, overrides) do filters = Keyword.get(overrides, :filters, socket.assigns.filters) @@ -427,8 +427,8 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do ) ) |> then(fn params -> - if map_update_mode == "auto", - do: Map.put(params, "map_update", "auto"), + if map_update_mode == "manual", + do: Map.put(params, "map_update", "manual"), else: params end) diff --git a/lib/who_need_help_web/live/request_live/index.ex b/lib/who_need_help_web/live/request_live/index.ex index a45b0ed..4e9ab3d 100644 --- a/lib/who_need_help_web/live/request_live/index.ex +++ b/lib/who_need_help_web/live/request_live/index.ex @@ -31,7 +31,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do |> assign(:discovery_mode, "nearby") |> assign(:view_mode, "split") |> assign(:view_explicit, false) - |> assign(:map_update_mode, "manual") + |> assign(:map_update_mode, "auto") |> assign(:viewport, nil) |> assign(:pending_viewport, nil) |> assign(:discovery_topics, []) @@ -379,7 +379,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do defp normalize_view_mode(_view_mode), do: "split" defp normalize_map_update_mode(mode) when mode in @map_update_modes, do: mode - defp normalize_map_update_mode(_mode), do: "manual" + defp normalize_map_update_mode(_mode), do: "auto" defp normalize_discovery_mode(discovery_mode) when discovery_mode in @discovery_modes, do: discovery_mode @@ -413,8 +413,8 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do ) ) |> then(fn params -> - if map_update_mode == "auto", - do: Map.put(params, "map_update", "auto"), + if map_update_mode == "manual", + do: Map.put(params, "map_update", "manual"), else: params end) end diff --git a/priv/repo/migrations/20260801200302_add_public_discovery_coordinates.exs b/priv/repo/migrations/20260801200302_add_public_discovery_coordinates.exs new file mode 100644 index 0000000..1cff6ae --- /dev/null +++ b/priv/repo/migrations/20260801200302_add_public_discovery_coordinates.exs @@ -0,0 +1,57 @@ +defmodule WhoNeedHelp.Repo.Migrations.AddPublicDiscoveryCoordinates do + use Ecto.Migration + + def change do + alter table(:help_requests) do + add :public_longitude, :float, + generated: """ + ALWAYS AS ( + CASE + WHEN location IS NULL OR location_visibility = 'hidden' THEN NULL + WHEN structured_data @> '{"scale_fixture": true}'::jsonb THEN ST_X(location) + WHEN location_visibility = 'exact_for_active_match' THEN + round(ST_X(location)::numeric, 2)::double precision + ELSE ST_X(location) + END + ) STORED + """ + + add :public_latitude, :float, + generated: """ + ALWAYS AS ( + CASE + WHEN location IS NULL OR location_visibility = 'hidden' THEN NULL + WHEN structured_data @> '{"scale_fixture": true}'::jsonb THEN ST_Y(location) + WHEN location_visibility = 'exact_for_active_match' THEN + round(ST_Y(location)::numeric, 2)::double precision + ELSE ST_Y(location) + END + ) STORED + """ + end + + alter table(:activities) do + add :public_longitude, :float, + generated: """ + ALWAYS AS ( + CASE + WHEN location IS NULL OR location_visibility = 'hidden' THEN NULL + WHEN structured_data @> '{"scale_fixture": true}'::jsonb THEN ST_X(location) + ELSE round(ST_X(location)::numeric, 2)::double precision + END + ) STORED + """ + + add :public_latitude, :float, + generated: """ + ALWAYS AS ( + CASE + WHEN location IS NULL OR location_visibility = 'hidden' THEN NULL + WHEN structured_data @> '{"scale_fixture": true}'::jsonb THEN ST_Y(location) + ELSE round(ST_Y(location)::numeric, 2)::double precision + END + ) STORED + """ + end + end +end diff --git a/test/who_need_help/help/discovery_cluster_test.exs b/test/who_need_help/help/discovery_cluster_test.exs index 27ae32f..1bbdb94 100644 --- a/test/who_need_help/help/discovery_cluster_test.exs +++ b/test/who_need_help/help/discovery_cluster_test.exs @@ -76,4 +76,22 @@ defmodule WhoNeedHelp.Help.DiscoveryClusterTest do assert DiscoveryCluster.expansion_zoom(level, west, south, west, south) == 22 assert DiscoveryCluster.expansion_zoom(24, west, south, west, south) == 22 end + + test "geographic bounds preserve Web Mercator expansion levels" do + assert DiscoveryCluster.expansion_zoom_for_bounds( + 7, + 30.50, + 50.40, + 30.60, + 50.50 + ) == 8 + + assert DiscoveryCluster.expansion_zoom_for_bounds( + 7, + 30.50, + 50.40, + 30.50, + 50.40 + ) == 22 + end end diff --git a/test/who_need_help_web/live/mutual_aid_live_test.exs b/test/who_need_help_web/live/mutual_aid_live_test.exs index b6dee4b..d63df16 100644 --- a/test/who_need_help_web/live/mutual_aid_live_test.exs +++ b/test/who_need_help_web/live/mutual_aid_live_test.exs @@ -385,7 +385,8 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do } do category = Catalog.seed_defaults() {:ok, request} = Help.create_request(scope, request_attrs(category)) - {:ok, view, _html} = live(conn, requests_in_kyiv_path()) + manual_path = requests_in_kyiv_path() <> "&map_update=manual" + {:ok, view, _html} = live(conn, manual_path) render_async(view) @@ -413,7 +414,7 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do assert_patch( view, - ~p"/requests?#{Map.merge(moved, %{"east" => "30.6", "north" => "50.5", "south" => "50.4", "view" => "split", "west" => "30.5"})}" + ~p"/requests?#{Map.merge(moved, %{"east" => "30.6", "map_update" => "manual", "north" => "50.5", "south" => "50.4", "view" => "split", "west" => "30.5"})}" ) render_async(view) @@ -421,7 +422,7 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do test "request discovery can update automatically after map movement", %{conn: conn} do path = - ~p"/requests?#{%{west: 30, south: 50, east: 31, north: 51, zoom: 10, width: 1200, height: 720, view: "split", map_update: "auto"}}" + ~p"/requests?#{%{west: 30, south: 50, east: 31, north: 51, zoom: 10, width: 1200, height: 720, view: "split"}}" {:ok, view, _html} = live(conn, path) render_async(view) @@ -444,7 +445,7 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do assert_patch( view, - ~p"/requests?#{Map.merge(moved, %{"east" => "30.6", "map_update" => "auto", "north" => "50.5", "south" => "50.4", "view" => "split", "west" => "30.5"})}", + ~p"/requests?#{Map.merge(moved, %{"east" => "30.6", "north" => "50.5", "south" => "50.4", "view" => "split", "west" => "30.5"})}", 1_000 ) @@ -454,6 +455,8 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do test "activity cluster expansion immediately searches the expanded viewport", %{conn: conn} do {:ok, view, _html} = live(conn, activities_in_kyiv_path()) + assert has_element?(view, "#activity-map-auto-update[checked]") + expanded = %{ "west" => "30.40", "south" => "50.35",