fix: prevent incoming packets from closing user-opened map popup
Add a client-side userOpenedPopupMarkerId lock that's set instantly when a user manually opens a popup (via click, OMS spiderfy click, or long-press) and cleared when the popup closes. All programmatic openPopup paths (highlight_packet, addMarker openPopup flag) now respect the lock. Closes the network round-trip race where new packets arriving between the user's click and the server's marker_clicked ack could steal the popup.
This commit is contained in:
parent
5e277ddb9d
commit
f005a1a119
2 changed files with 22 additions and 6 deletions
|
|
@ -682,6 +682,10 @@ let MapAPRSMap = {
|
||||||
|
|
||||||
// Add click handler for spiderfied markers
|
// Add click handler for spiderfied markers
|
||||||
self.oms.addListener("click", function (marker: Marker) {
|
self.oms.addListener("click", function (marker: Marker) {
|
||||||
|
const aprsMarker = marker as APRSMarker;
|
||||||
|
if (aprsMarker._markerId) {
|
||||||
|
self.userOpenedPopupMarkerId = aprsMarker._markerId;
|
||||||
|
}
|
||||||
if (marker.openPopup) marker.openPopup();
|
if (marker.openPopup) marker.openPopup();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -760,6 +764,10 @@ let MapAPRSMap = {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (nearestMarker) {
|
if (nearestMarker) {
|
||||||
|
const aprsMarker = nearestMarker as APRSMarker;
|
||||||
|
if (aprsMarker._markerId) {
|
||||||
|
self.userOpenedPopupMarkerId = aprsMarker._markerId;
|
||||||
|
}
|
||||||
nearestMarker.openPopup();
|
nearestMarker.openPopup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1263,8 +1271,10 @@ let MapAPRSMap = {
|
||||||
|
|
||||||
// Handle highlighting the latest packet (open its popup)
|
// Handle highlighting the latest packet (open its popup)
|
||||||
self.currentPopupMarkerId = null;
|
self.currentPopupMarkerId = null;
|
||||||
|
self.userOpenedPopupMarkerId = null;
|
||||||
self.handleEvent("highlight_packet", (data: { id: string }) => {
|
self.handleEvent("highlight_packet", (data: { id: string }) => {
|
||||||
if (!data.id || !self.markers || !self.markerStates) return;
|
if (!data.id || !self.markers || !self.markerStates) return;
|
||||||
|
if (self.userOpenedPopupMarkerId) return;
|
||||||
|
|
||||||
// Close previous popup if open
|
// Close previous popup if open
|
||||||
if (
|
if (
|
||||||
|
|
@ -1843,8 +1853,8 @@ let MapAPRSMap = {
|
||||||
|
|
||||||
if (!positionChanged && !dataChanged) {
|
if (!positionChanged && !dataChanged) {
|
||||||
// No changes needed, skip update
|
// No changes needed, skip update
|
||||||
// But if openPopup is requested, open it
|
// But if openPopup is requested and no user popup is locked, open it
|
||||||
if (data.openPopup && existingMarker.openPopup) {
|
if (data.openPopup && existingMarker.openPopup && !self.userOpenedPopupMarkerId) {
|
||||||
existingMarker.openPopup();
|
existingMarker.openPopup();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -1898,6 +1908,7 @@ let MapAPRSMap = {
|
||||||
const marker = L.marker([lat, lng], {
|
const marker = L.marker([lat, lng], {
|
||||||
icon: self.createMarkerIcon(data),
|
icon: self.createMarkerIcon(data),
|
||||||
});
|
});
|
||||||
|
(marker as APRSMarker)._markerId = data.id;
|
||||||
|
|
||||||
// Add popup if content provided
|
// Add popup if content provided
|
||||||
if (data.popup) {
|
if (data.popup) {
|
||||||
|
|
@ -1905,7 +1916,9 @@ let MapAPRSMap = {
|
||||||
|
|
||||||
// Handle popup close events - check if hook is still connected
|
// Handle popup close events - check if hook is still connected
|
||||||
marker.on("popupclose", () => {
|
marker.on("popupclose", () => {
|
||||||
// Only send event if not destroyed and pushEvent is still the original function
|
if (self.userOpenedPopupMarkerId === data.id) {
|
||||||
|
self.userOpenedPopupMarkerId = null;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
!self.isDestroyed &&
|
!self.isDestroyed &&
|
||||||
self.pushEvent &&
|
self.pushEvent &&
|
||||||
|
|
@ -1914,7 +1927,6 @@ let MapAPRSMap = {
|
||||||
try {
|
try {
|
||||||
self.pushEvent("popup_closed", {});
|
self.pushEvent("popup_closed", {});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Silently ignore errors if context is lost
|
|
||||||
console.debug("Unable to send popup_closed event:", e);
|
console.debug("Unable to send popup_closed event:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1927,9 +1939,11 @@ let MapAPRSMap = {
|
||||||
// The OMS will handle the click and open the popup after spiderfying
|
// The OMS will handle the click and open the popup after spiderfying
|
||||||
if (self.oms && marker._oms && marker._omsData) {
|
if (self.oms && marker._oms && marker._omsData) {
|
||||||
// Marker is currently spiderfied, allow normal popup
|
// Marker is currently spiderfied, allow normal popup
|
||||||
|
self.userOpenedPopupMarkerId = data.id;
|
||||||
if (marker.openPopup) marker.openPopup();
|
if (marker.openPopup) marker.openPopup();
|
||||||
} else if (!self.oms || !marker._oms) {
|
} else if (!self.oms || !marker._oms) {
|
||||||
// Normal marker not managed by OMS
|
// Normal marker not managed by OMS
|
||||||
|
self.userOpenedPopupMarkerId = data.id;
|
||||||
if (marker.openPopup) marker.openPopup();
|
if (marker.openPopup) marker.openPopup();
|
||||||
}
|
}
|
||||||
// If marker._oms is true but no _omsData, let OMS handle it (will spiderfy)
|
// If marker._oms is true but no _omsData, let OMS handle it (will spiderfy)
|
||||||
|
|
@ -2059,8 +2073,8 @@ let MapAPRSMap = {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open popup if requested
|
// Open popup if requested and no user popup is locked
|
||||||
if (data.openPopup && marker.openPopup) {
|
if (data.openPopup && marker.openPopup && !self.userOpenedPopupMarkerId) {
|
||||||
marker.openPopup();
|
marker.openPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
assets/js/types/map.d.ts
vendored
2
assets/js/types/map.d.ts
vendored
|
|
@ -23,6 +23,8 @@ export interface LiveViewHookContext {
|
||||||
maxInitializationAttempts?: number;
|
maxInitializationAttempts?: number;
|
||||||
lastZoom?: number;
|
lastZoom?: number;
|
||||||
currentPopupMarkerId?: string | null;
|
currentPopupMarkerId?: string | null;
|
||||||
|
userOpenedPopupMarkerId?: string | null;
|
||||||
|
markerZIndexCounter?: number;
|
||||||
oms?: OverlappingMarkerSpiderfier;
|
oms?: OverlappingMarkerSpiderfier;
|
||||||
programmaticMoveId?: string;
|
programmaticMoveId?: string;
|
||||||
programmaticMoveTimeout?: ReturnType<typeof setTimeout>;
|
programmaticMoveTimeout?: ReturnType<typeof setTimeout>;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue