Fix spurious bounds_changed events and event loops
Key fixes: - Combine bounds_changed and update_map_state into single event - Remove duplicate sendBoundsToServer() calls - Add programmaticMove flag to prevent event loops - Only process bounds updates when bounds actually change - Debounce events with 300ms timeout This eliminates: - Duplicate server events for same user action - Event loops when server updates URL - Unnecessary bounds processing - Excessive server load 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
c873614bf1
commit
a9414823f1
2 changed files with 50 additions and 7 deletions
|
|
@ -25,6 +25,7 @@ type LiveViewHookContext = {
|
||||||
lastZoom?: number;
|
lastZoom?: number;
|
||||||
currentPopupMarkerId?: string | null;
|
currentPopupMarkerId?: string | null;
|
||||||
oms?: any;
|
oms?: any;
|
||||||
|
programmaticMove?: boolean;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -287,9 +288,14 @@ let MapAPRSMap = {
|
||||||
|
|
||||||
// Send bounds to LiveView when map moves
|
// Send bounds to LiveView when map moves
|
||||||
self.map!.on("moveend", () => {
|
self.map!.on("moveend", () => {
|
||||||
|
// Skip if this is a programmatic move from the server
|
||||||
|
if (self.programmaticMove) {
|
||||||
|
self.programmaticMove = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self.boundsTimer) clearTimeout(self.boundsTimer);
|
if (self.boundsTimer) clearTimeout(self.boundsTimer);
|
||||||
self.boundsTimer = setTimeout(() => {
|
self.boundsTimer = setTimeout(() => {
|
||||||
self.sendBoundsToServer();
|
|
||||||
// Save map state and update URL
|
// Save map state and update URL
|
||||||
const center = self.map.getCenter();
|
const center = self.map.getCenter();
|
||||||
const zoom = self.map.getZoom();
|
const zoom = self.map.getZoom();
|
||||||
|
|
@ -298,16 +304,28 @@ let MapAPRSMap = {
|
||||||
JSON.stringify({ lat: center.lat, lng: center.lng, zoom }),
|
JSON.stringify({ lat: center.lat, lng: center.lng, zoom }),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send map state update to server for URL updating
|
// Send combined map state update to server for URL and bounds updating
|
||||||
self.pushEvent("update_map_state", {
|
self.pushEvent("update_map_state", {
|
||||||
center: { lat: center.lat, lng: center.lng },
|
center: { lat: center.lat, lng: center.lng },
|
||||||
zoom: zoom
|
zoom: zoom,
|
||||||
|
bounds: {
|
||||||
|
north: self.map.getBounds().getNorth(),
|
||||||
|
south: self.map.getBounds().getSouth(),
|
||||||
|
east: self.map.getBounds().getEast(),
|
||||||
|
west: self.map.getBounds().getWest(),
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle zoom changes with optimization for large zoom differences
|
// Handle zoom changes with optimization for large zoom differences
|
||||||
self.map!.on("zoomend", () => {
|
self.map!.on("zoomend", () => {
|
||||||
|
// Skip if this is a programmatic move from the server
|
||||||
|
if (self.programmaticMove) {
|
||||||
|
self.programmaticMove = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self.boundsTimer) clearTimeout(self.boundsTimer);
|
if (self.boundsTimer) clearTimeout(self.boundsTimer);
|
||||||
self.boundsTimer = setTimeout(() => {
|
self.boundsTimer = setTimeout(() => {
|
||||||
const currentZoom = self.map!.getZoom();
|
const currentZoom = self.map!.getZoom();
|
||||||
|
|
@ -319,7 +337,6 @@ let MapAPRSMap = {
|
||||||
self.pushEvent("refresh_markers", {});
|
self.pushEvent("refresh_markers", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sendBoundsToServer();
|
|
||||||
self.lastZoom = currentZoom;
|
self.lastZoom = currentZoom;
|
||||||
// Save map state and update URL
|
// Save map state and update URL
|
||||||
const center = self.map.getCenter();
|
const center = self.map.getCenter();
|
||||||
|
|
@ -329,10 +346,16 @@ let MapAPRSMap = {
|
||||||
JSON.stringify({ lat: center.lat, lng: center.lng, zoom }),
|
JSON.stringify({ lat: center.lat, lng: center.lng, zoom }),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send map state update to server for URL updating
|
// Send combined map state update to server for URL and bounds updating
|
||||||
self.pushEvent("update_map_state", {
|
self.pushEvent("update_map_state", {
|
||||||
center: { lat: center.lat, lng: center.lng },
|
center: { lat: center.lat, lng: center.lng },
|
||||||
zoom: zoom
|
zoom: zoom,
|
||||||
|
bounds: {
|
||||||
|
north: self.map.getBounds().getNorth(),
|
||||||
|
south: self.map.getBounds().getSouth(),
|
||||||
|
east: self.map.getBounds().getEast(),
|
||||||
|
west: self.map.getBounds().getWest(),
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
|
|
@ -464,6 +487,8 @@ let MapAPRSMap = {
|
||||||
// Use a slight delay to ensure map is ready
|
// Use a slight delay to ensure map is ready
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (self.map) {
|
if (self.map) {
|
||||||
|
// Set flag to prevent event loop
|
||||||
|
self.programmaticMove = true;
|
||||||
self.map.setView([lat, lng], zoom, {
|
self.map.setView([lat, lng], zoom, {
|
||||||
animate: true,
|
animate: true,
|
||||||
duration: 1,
|
duration: 1,
|
||||||
|
|
|
||||||
|
|
@ -317,7 +317,7 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("update_map_state", %{"center" => center, "zoom" => zoom}, socket) do
|
def handle_event("update_map_state", %{"center" => center, "zoom" => zoom} = params, socket) do
|
||||||
# Parse center coordinates
|
# Parse center coordinates
|
||||||
lat =
|
lat =
|
||||||
case center do
|
case center do
|
||||||
|
|
@ -345,6 +345,24 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
new_path = "/?lat=#{lat}&lng=#{lng}&z=#{zoom}"
|
new_path = "/?lat=#{lat}&lng=#{lng}&z=#{zoom}"
|
||||||
socket = push_patch(socket, to: new_path, replace: true)
|
socket = push_patch(socket, to: new_path, replace: true)
|
||||||
|
|
||||||
|
# If bounds are included, also process bounds update
|
||||||
|
socket = case Map.get(params, "bounds") do
|
||||||
|
%{"north" => north, "south" => south, "east" => east, "west" => west} ->
|
||||||
|
map_bounds = %{
|
||||||
|
north: north,
|
||||||
|
south: south,
|
||||||
|
east: east,
|
||||||
|
west: west
|
||||||
|
}
|
||||||
|
# Only trigger bounds processing if bounds actually changed
|
||||||
|
if socket.assigns.map_bounds != map_bounds do
|
||||||
|
send(self(), {:process_bounds_update, map_bounds})
|
||||||
|
end
|
||||||
|
socket
|
||||||
|
_ ->
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue