mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-04-23 19:16:06 +02:00
fix: replace array-index entity IDs with stable keys for GDELT and news popups
selectedEntity.id was stored as a numeric array index into data.gdelt[] and data.news[]. After any data refresh those arrays rebuild, so the stored index pointed to a different item — showing wrong popup content. GDELT features now use g.properties?.name || String(g.geometry.coordinates) as a stable id; popups resolve via find(). News popups resolve via find() matching alertKey. ThreatMarkers emits alertKey string instead of originalIdx. ThreatMarkerProps updated: id: number → id: string | number. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Former-commit-id: c2bfd0897a9ebd27e7c905ea3ac848a89883f140
This commit is contained in:
@@ -1037,7 +1037,7 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
if (!inView(gLat, gLng)) return null;
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: { id: i, type: 'gdelt', title: g.title },
|
||||
properties: { id: g.properties?.name || String(g.geometry.coordinates), type: 'gdelt', title: g.title },
|
||||
geometry: g.geometry
|
||||
};
|
||||
}).filter(Boolean)
|
||||
@@ -2232,10 +2232,13 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
})()}
|
||||
|
||||
{
|
||||
selectedEntity?.type === 'gdelt' && data?.gdelt?.[selectedEntity.id as number] && (
|
||||
selectedEntity?.type === 'gdelt' && (() => {
|
||||
const item = data?.gdelt?.find((g: any) => (g.properties?.name || String(g.geometry?.coordinates)) === selectedEntity.id);
|
||||
if (!item) return null;
|
||||
return (
|
||||
<Popup
|
||||
longitude={data.gdelt[selectedEntity.id as number].geometry.coordinates[0]}
|
||||
latitude={data.gdelt[selectedEntity.id as number].geometry.coordinates[1]}
|
||||
longitude={item.geometry.coordinates[0]}
|
||||
latitude={item.geometry.coordinates[1]}
|
||||
closeButton={false}
|
||||
closeOnClick={false}
|
||||
onClose={() => onEntityClick?.(null)}
|
||||
@@ -2252,14 +2255,14 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
<div className="p-3 flex flex-col gap-2">
|
||||
<div className="flex justify-between items-center border-b border-[var(--border-primary)] pb-1">
|
||||
<span className="text-[var(--text-muted)] text-[9px]">LOCATION</span>
|
||||
<span className="text-white text-[10px] font-bold text-right ml-2 break-words max-w-[150px]">{data.gdelt[selectedEntity.id as number].properties?.name || 'UNKNOWN REGION'}</span>
|
||||
<span className="text-white text-[10px] font-bold text-right ml-2 break-words max-w-[150px]">{item.properties?.name || 'UNKNOWN REGION'}</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 mt-1">
|
||||
<span className="text-[var(--text-muted)] text-[9px]">LATEST REPORTS: ({data.gdelt[selectedEntity.id as number].properties?.count || 1})</span>
|
||||
<span className="text-[var(--text-muted)] text-[9px]">LATEST REPORTS: ({item.properties?.count || 1})</span>
|
||||
<div className="flex flex-col gap-2 max-h-[200px] overflow-y-auto styled-scrollbar mt-1">
|
||||
{(() => {
|
||||
const urls: string[] = data.gdelt[selectedEntity.id as number].properties?._urls_list || [];
|
||||
const headlines: string[] = data.gdelt[selectedEntity.id as number].properties?._headlines_list || [];
|
||||
const urls: string[] = item.properties?._urls_list || [];
|
||||
const headlines: string[] = item.properties?._headlines_list || [];
|
||||
if (urls.length === 0) return <span className="text-[var(--text-muted)] text-[10px]">No articles available.</span>;
|
||||
return urls.map((url: string, idx: number) => {
|
||||
const headline = headlines[idx] || '';
|
||||
@@ -2290,7 +2293,8 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
)
|
||||
);
|
||||
})()
|
||||
}
|
||||
|
||||
{
|
||||
@@ -2336,8 +2340,8 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
}
|
||||
|
||||
{
|
||||
selectedEntity?.type === 'news' && data?.news?.[selectedEntity.id as number] && (() => {
|
||||
const item = data.news[selectedEntity.id as number];
|
||||
selectedEntity?.type === 'news' && (() => {
|
||||
const item = data?.news?.find((n: any) => (n.alertKey || `${n.title}|${n.coords?.[0]},${n.coords?.[1]}`) === selectedEntity.id);
|
||||
let threatColor = "text-yellow-400";
|
||||
let borderColor = "border-yellow-800";
|
||||
let bgHeaderColor = "bg-yellow-950/40";
|
||||
|
||||
@@ -168,7 +168,7 @@ interface ThreatMarkerProps {
|
||||
spreadAlerts: any[];
|
||||
viewState: ViewState;
|
||||
selectedEntity: any;
|
||||
onEntityClick?: (entity: { id: number; type: string } | null) => void;
|
||||
onEntityClick?: (entity: { id: string | number; type: string } | null) => void;
|
||||
onDismiss?: (alertKey: string) => void;
|
||||
}
|
||||
|
||||
@@ -176,22 +176,20 @@ export function ThreatMarkers({ spreadAlerts, viewState, selectedEntity, onEntit
|
||||
return (
|
||||
<>
|
||||
{spreadAlerts.map((n: any) => {
|
||||
const idx = n.originalIdx;
|
||||
const count = n.cluster_count || 1;
|
||||
const score = n.risk_score || 0;
|
||||
const riskColor = getRiskColor(score);
|
||||
const alertKey = n.alertKey || `${n.title}|${n.coords?.[0]},${n.coords?.[1]}`;
|
||||
|
||||
let isVisible = viewState.zoom >= 1;
|
||||
if (selectedEntity) {
|
||||
if (selectedEntity.type === 'news') {
|
||||
if (selectedEntity.id !== idx) isVisible = false;
|
||||
if (selectedEntity.id !== alertKey) isVisible = false;
|
||||
} else {
|
||||
isVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
const alertKey = n.alertKey || `${n.title}|${n.coords?.[0]},${n.coords?.[1]}`;
|
||||
|
||||
return (
|
||||
<Marker
|
||||
key={`threat-${alertKey}`}
|
||||
@@ -202,7 +200,7 @@ export function ThreatMarkers({ spreadAlerts, viewState, selectedEntity, onEntit
|
||||
style={{ zIndex: 50 + score }}
|
||||
onClick={(e) => {
|
||||
e.originalEvent.stopPropagation();
|
||||
onEntityClick?.({ id: idx, type: 'news' });
|
||||
onEntityClick?.({ id: alertKey, type: 'news' });
|
||||
}}
|
||||
>
|
||||
<div className="relative group/alert">
|
||||
|
||||
Reference in New Issue
Block a user