]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/5826'
authorTom Hughes <tom@compton.nu>
Sun, 23 Mar 2025 19:42:27 +0000 (19:42 +0000)
committerTom Hughes <tom@compton.nu>
Sun, 23 Mar 2025 19:42:27 +0000 (19:42 +0000)
app/assets/javascripts/index/history.js
test/system/history_test.rb

index 229fdc2b609901b672a72a03769c47d71ba4f777..9f883367a799393be5cc2d2b3a20dfbda48271e7 100644 (file)
@@ -4,7 +4,7 @@ OSM.History = function (map) {
   const page = {};
 
   $("#sidebar_content")
-    .on("click", ".changeset_more a", loadMore)
+    .on("click", ".changeset_more a", loadMoreChangesets)
     .on("mouseover", "[data-changeset]", function () {
       highlightChangeset($(this).data("changeset").id);
     })
@@ -27,6 +27,65 @@ OSM.History = function (map) {
     return layer.id;
   };
 
+  let changesetIntersectionObserver;
+
+  function disableChangesetIntersectionObserver() {
+    if (changesetIntersectionObserver) {
+      changesetIntersectionObserver.disconnect();
+      changesetIntersectionObserver = null;
+    }
+  }
+
+  function enableChangesetIntersectionObserver() {
+    disableChangesetIntersectionObserver();
+    if (!window.IntersectionObserver) return;
+
+    let ignoreIntersectionEvents = true;
+
+    changesetIntersectionObserver = new IntersectionObserver((entries) => {
+      if (ignoreIntersectionEvents) {
+        ignoreIntersectionEvents = false;
+        return;
+      }
+
+      let closestTargetToTop,
+          closestDistanceToTop = Infinity,
+          closestTargetToBottom,
+          closestDistanceToBottom = Infinity;
+
+      for (const entry of entries) {
+        if (entry.isIntersecting) continue;
+
+        const distanceToTop = entry.rootBounds.top - entry.boundingClientRect.bottom;
+        const distanceToBottom = entry.boundingClientRect.top - entry.rootBounds.bottom;
+        if (distanceToTop >= 0 && distanceToTop < closestDistanceToTop) {
+          closestDistanceToTop = distanceToTop;
+          closestTargetToTop = entry.target;
+        }
+        if (distanceToBottom >= 0 && distanceToBottom <= closestDistanceToBottom) {
+          closestDistanceToBottom = distanceToBottom;
+          closestTargetToBottom = entry.target;
+        }
+      }
+
+      if (closestTargetToTop && closestDistanceToTop < closestDistanceToBottom) {
+        const id = $(closestTargetToTop).data("changeset")?.id;
+        if (id) {
+          OSM.router.replace(location.pathname + "?" + new URLSearchParams({ before: id }) + location.hash);
+        }
+      } else if (closestTargetToBottom) {
+        const id = $(closestTargetToBottom).data("changeset")?.id;
+        if (id) {
+          OSM.router.replace(location.pathname + "?" + new URLSearchParams({ after: id }) + location.hash);
+        }
+      }
+    }, { root: $("#sidebar")[0] });
+
+    $("#sidebar_content .changesets ol").children().each(function () {
+      changesetIntersectionObserver.observe(this);
+    });
+  }
+
   function highlightChangeset(id) {
     const layer = group.getLayer(id);
     if (layer) layer.setStyle({ fillOpacity: 0.3, color: "#FF6600", weight: 3 });
@@ -73,10 +132,12 @@ OSM.History = function (map) {
     }
   }
 
-  function update() {
+  function loadFirstChangesets() {
     const data = new URLSearchParams();
     const params = new URLSearchParams(location.search);
 
+    disableChangesetIntersectionObserver();
+
     if (location.pathname === "/history") {
       data.set("bbox", map.getBounds().wrap().toBBoxString());
       const feedLink = $("link[type=\"application/atom+xml\"]"),
@@ -97,11 +158,22 @@ OSM.History = function (map) {
       .then(response => response.text())
       .then(function (html) {
         displayFirstChangesets(html);
+        enableChangesetIntersectionObserver();
+
+        if (params.has("before")) {
+          const [firstItem] = $("#sidebar_content .changesets ol").children().first();
+          firstItem?.scrollIntoView();
+        }
+        if (params.has("after")) {
+          const [lastItem] = $("#sidebar_content .changesets ol").children().last();
+          lastItem?.scrollIntoView(false);
+        }
+
         updateMap();
       });
   }
 
-  function loadMore(e) {
+  function loadMoreChangesets(e) {
     e.preventDefault();
     e.stopPropagation();
 
@@ -112,10 +184,16 @@ OSM.History = function (map) {
 
     $.get($(this).attr("href"), function (html) {
       displayMoreChangesets(div, html);
+      enableChangesetIntersectionObserver();
       updateMap();
     });
   }
 
+  function reloadChangesetsBecauseOfMapMovement() {
+    OSM.router.replace("/history" + window.location.hash);
+    loadFirstChangesets();
+  }
+
   let changesets = [];
 
   function updateBounds() {
@@ -177,18 +255,19 @@ OSM.History = function (map) {
     map.addLayer(group);
 
     if (location.pathname === "/history") {
-      map.on("moveend", update);
+      map.on("moveend", reloadChangesetsBecauseOfMapMovement);
     }
 
     map.on("zoomend", updateBounds);
 
-    update();
+    loadFirstChangesets();
   };
 
   page.unload = function () {
     map.removeLayer(group);
-    map.off("moveend", update);
+    map.off("moveend", reloadChangesetsBecauseOfMapMovement);
     map.off("zoomend", updateBounds);
+    disableChangesetIntersectionObserver();
   };
 
   return page;
index 7d730355a083c9b327cd6452b3dd4aca96f216e8..be7700c3b893699f229feab865c56853f589eb84 100644 (file)
@@ -109,6 +109,30 @@ class HistoryTest < ApplicationSystemTestCase
     end
   end
 
+  test "update sidebar when before param is included and map is moved" do
+    changeset1 = create(:changeset, :num_changes => 1, :min_lat => 50000000, :max_lat => 50000001, :min_lon => 50000000, :max_lon => 50000001)
+    create(:changeset_tag, :changeset => changeset1, :k => "comment", :v => "changeset-at-fives")
+    changeset2 = create(:changeset, :num_changes => 1, :min_lat => 50100000, :max_lat => 50100001, :min_lon => 50100000, :max_lon => 50100001)
+    create(:changeset_tag, :changeset => changeset2, :k => "comment", :v => "changeset-close-to-fives")
+    changeset3 = create(:changeset)
+
+    visit "/history?before=#{changeset3.id}#map=17/5/5"
+
+    within_sidebar do
+      assert_link "changeset-at-fives"
+      assert_no_link "changeset-close-to-fives"
+    end
+
+    find("#map [aria-label='Zoom Out']").click(:shift)
+
+    within_sidebar do
+      assert_link "changeset-at-fives"
+      assert_link "changeset-close-to-fives"
+    end
+
+    assert_current_path history_path
+  end
+
   private
 
   def create_visible_changeset(user, comment)