diff --git a/src/components/AdvertDetails.tsx b/src/components/AdvertDetails.tsx
index 8e9705e..03ad0a4 100644
--- a/src/components/AdvertDetails.tsx
+++ b/src/components/AdvertDetails.tsx
@@ -17,6 +17,7 @@ interface AdvertDetailsProps {
is_chat_node: number;
is_room_server: number;
has_location: number;
+ packet_hash: string;
};
initiatingNodeKey?: string;
}
@@ -97,6 +98,7 @@ export default function AdvertDetails({ advert, initiatingNodeKey }: AdvertDetai
}))}
className="text-sm"
initiatingNodeKey={initiatingNodeKey}
+ packetHash={advert.packet_hash}
/>
diff --git a/src/components/ChatMessageItem.tsx b/src/components/ChatMessageItem.tsx
index 22cfee8..146f4a2 100644
--- a/src/components/ChatMessageItem.tsx
+++ b/src/components/ChatMessageItem.tsx
@@ -160,6 +160,7 @@ function ChatMessageItem({ msg, showErrorRow }: { msg: ChatMessage, showErrorRow
paths={pathData}
title={`Heard ${pathData.length} repeat${pathData.length !== 1 ? 's' : ''}`}
className="text-xs"
+ packetHash={msg.message_id}
/>
);
@@ -180,6 +181,7 @@ function ChatMessageItem({ msg, showErrorRow }: { msg: ChatMessage, showErrorRow
paths={pathData}
title={`Heard ${pathData.length} repeat${pathData.length !== 1 ? 's' : ''}`}
className="text-xs"
+ packetHash={msg.message_id}
/>
);
@@ -200,6 +202,7 @@ function ChatMessageItem({ msg, showErrorRow }: { msg: ChatMessage, showErrorRow
paths={pathData}
title={`Heard ${pathData.length} repeat${pathData.length !== 1 ? 's' : ''}`}
className="text-xs"
+ packetHash={msg.message_id}
/>
);
diff --git a/src/components/PathVisualization.tsx b/src/components/PathVisualization.tsx
index 1609153..85f1774 100644
--- a/src/components/PathVisualization.tsx
+++ b/src/components/PathVisualization.tsx
@@ -5,6 +5,7 @@ import { createPortal } from "react-dom";
import Link from "next/link";
import Tree from 'react-d3-tree';
import { ArrowsPointingOutIcon, ArrowsPointingInIcon } from "@heroicons/react/24/outline";
+import { ExternalLink } from "lucide-react";
import PathDisplay from "./PathDisplay";
import { useMeshcoreSearches } from "@/hooks/useMeshcoreSearch";
import type { MeshcoreSearchResult } from "@/hooks/useMeshcoreSearch";
@@ -33,6 +34,7 @@ interface PathVisualizationProps {
className?: string;
showDropdown?: boolean;
initiatingNodeKey?: string;
+ packetHash?: string;
}
@@ -41,7 +43,8 @@ export default function PathVisualization({
title = "Paths",
className = "",
showDropdown = true,
- initiatingNodeKey
+ initiatingNodeKey,
+ packetHash
}: PathVisualizationProps) {
const [expanded, setExpanded] = useState(false);
const [showGraph, setShowGraph] = useState(false);
@@ -340,6 +343,17 @@ export default function PathVisualization({
{showGraph ? 'Hide Graph' : 'Show Graph'}
)}
+ {packetHash && (
+
+ Analyze
+
+
+ )}
{showGraph && }
@@ -373,6 +387,18 @@ export default function PathVisualization({
{showGraph ? 'Hide Graph' : 'Show Graph'}
)}
+
+ {packetHash && (
+
+ Analyze
+
+
+ )}
{expanded && pathsCount > 0 && (
diff --git a/src/hooks/useNodeData.ts b/src/hooks/useNodeData.ts
index 226e8ff..a5cfd49 100644
--- a/src/hooks/useNodeData.ts
+++ b/src/hooks/useNodeData.ts
@@ -29,6 +29,7 @@ export interface Advert {
is_chat_node: number;
is_room_server: number;
has_location: number;
+ packet_hash: string;
}
export interface LocationHistory {
diff --git a/src/lib/clickhouse/actions.ts b/src/lib/clickhouse/actions.ts
index 2aab405..581fb8c 100644
--- a/src/lib/clickhouse/actions.ts
+++ b/src/lib/clickhouse/actions.ts
@@ -157,7 +157,7 @@ export async function getMeshcoreNodeInfo(publicKey: string, limit: number = 50)
// Get recent adverts grouped by adv_timestamp with origin_path_pubkey tuples
const advertsQuery = `
SELECT
- adv_timestamp,
+ argMax(adv_timestamp, ingest_timestamp) as adv_timestamp,
groupArray((origin, path, origin_pubkey)) as origin_path_pubkey_tuples,
count() as advert_count,
min(ingest_timestamp) as earliest_timestamp,
@@ -167,7 +167,8 @@ export async function getMeshcoreNodeInfo(publicKey: string, limit: number = 50)
argMax(is_repeater, ingest_timestamp) as is_repeater,
argMax(is_chat_node, ingest_timestamp) as is_chat_node,
argMax(is_room_server, ingest_timestamp) as is_room_server,
- argMax(has_location, ingest_timestamp) as has_location
+ argMax(has_location, ingest_timestamp) as has_location,
+ packet_hash
FROM (
SELECT
ingest_timestamp,
@@ -182,12 +183,13 @@ export async function getMeshcoreNodeInfo(publicKey: string, limit: number = 50)
is_room_server,
has_location,
hex(origin_pubkey) as origin_pubkey,
- origin
+ origin,
+ packet_hash
FROM meshcore_adverts
WHERE public_key = {publicKey:String}
ORDER BY ingest_timestamp DESC
)
- GROUP BY adv_timestamp
+ GROUP BY packet_hash
ORDER BY max(ingest_timestamp) DESC
LIMIT {limit:UInt32}
`;