mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-09-01 16:48:19 +00:00
Removed observations dropdown from main packet list, moved completely to packet analyzer
This commit is contained in:
+1
-1
@@ -96,7 +96,7 @@ function AppInner() {
|
||||
}, []);
|
||||
|
||||
const tabContent: Record<string, React.ReactNode> = {
|
||||
Packets: <PacketList wsManager={wsManager} onAnalyze={handleAnalyze} selectedObservationId={selectedObservationId} onSelectObservation={setSelectedObservationId} />,
|
||||
Packets: <PacketList wsManager={wsManager} onAnalyze={handleAnalyze} />,
|
||||
Nodes: <NodeTable wsManager={wsManager} />,
|
||||
Observers: <ObserverTable wsManager={wsManager} />,
|
||||
Channels: <ChannelList wsManager={wsManager} onAnalyze={handleAnalyze} />,
|
||||
|
||||
@@ -149,7 +149,7 @@ export function PacketAnalyzerDrawer({ detail, selectedObservationId, open, onTo
|
||||
</DrawerSection>
|
||||
)}
|
||||
|
||||
{detail.observations.length > 1 && (
|
||||
{detail.observations.length >= 1 && (
|
||||
<DrawerSection title={`Observations (${detail.observations.length})`} collapsible defaultOpen={false}>
|
||||
<div className="flex flex-col gap-1">
|
||||
{detail.observations.map((obs) => (
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { useState, useCallback, useRef, useMemo } from "react";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { useQuery, keepPreviousData } from "@tanstack/react-query";
|
||||
import { usePackets } from "./usePackets";
|
||||
import { usePacketFilters, matchesFilters } from "./usePacketFilters";
|
||||
import { useWsPacketHandler, useWsLaggedHandler } from "../../hooks/useWsHandlers";
|
||||
import { PacketVirtualList } from "./PacketVirtualList";
|
||||
import { FilterBar } from "../../components/FilterBar";
|
||||
import { getPacketDetail } from "../../api/client";
|
||||
import { PAYLOAD_TYPE_NAMES, ROUTE_TYPE_NAMES } from "../../types/enums";
|
||||
import type { WsManager } from "../../api/ws-manager";
|
||||
|
||||
@@ -25,13 +23,11 @@ const ROUTE_OPTIONS = Object.entries(ROUTE_TYPE_NAMES).map(([value, label]) => (
|
||||
interface PacketListProps {
|
||||
wsManager: WsManager;
|
||||
onAnalyze: (hash: string | null) => void;
|
||||
selectedObservationId: number | null;
|
||||
onSelectObservation: (id: number | null) => void;
|
||||
}
|
||||
|
||||
// main packet view: filters, banner, virtual list
|
||||
|
||||
export function PacketList({ wsManager, onAnalyze, selectedObservationId, onSelectObservation }: PacketListProps) {
|
||||
export function PacketList({ wsManager, onAnalyze }: PacketListProps) {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { filters, setFilter, setSearch, setSearchField, clearFilters } = usePacketFilters();
|
||||
const {
|
||||
@@ -59,26 +55,16 @@ export function PacketList({ wsManager, onAnalyze, selectedObservationId, onSele
|
||||
|
||||
const [expandedHash, setExpandedHash] = useState<string | null>(() => searchParams.get("hash"));
|
||||
|
||||
const { data: expandedDetail } = useQuery({
|
||||
queryKey: ["packet-detail", expandedHash],
|
||||
queryFn: () => getPacketDetail(expandedHash!),
|
||||
enabled: !!expandedHash,
|
||||
staleTime: Infinity,
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
|
||||
const handleToggleExpand = useCallback((hash: string) => {
|
||||
setExpandedHash((prev) => {
|
||||
const next = prev === hash ? null : hash;
|
||||
onAnalyze(next);
|
||||
setSearchParams((p) => {
|
||||
const n = new URLSearchParams(p);
|
||||
if (next) n.set("hash", next); else n.delete("hash");
|
||||
return n;
|
||||
}, { replace: true });
|
||||
return next;
|
||||
});
|
||||
}, [setSearchParams, onAnalyze]);
|
||||
const next = expandedHash === hash ? null : hash;
|
||||
setExpandedHash(next);
|
||||
onAnalyze(next);
|
||||
setSearchParams((p) => {
|
||||
const n = new URLSearchParams(p);
|
||||
if (next) n.set("hash", next); else n.delete("hash");
|
||||
return n;
|
||||
}, { replace: true });
|
||||
}, [expandedHash, setSearchParams, onAnalyze]);
|
||||
|
||||
useWsPacketHandler(wsManager, handlePacketObservation);
|
||||
useWsLaggedHandler(wsManager, handleLagged);
|
||||
@@ -143,10 +129,7 @@ export function PacketList({ wsManager, onAnalyze, selectedObservationId, onSele
|
||||
onScrollAwayFromTop={handleScrolledAway}
|
||||
scrollToTopRef={scrollToTopRef}
|
||||
expandedHash={expandedHash}
|
||||
expandedDetail={expandedDetail}
|
||||
onToggleExpand={handleToggleExpand}
|
||||
selectedObservationId={selectedObservationId}
|
||||
onSelectObservation={onSelectObservation}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
import { formatHex, formatTimestamp } from "../../lib/formatters";
|
||||
import type { PacketSummary, PacketDetail } from "../../types/api";
|
||||
import type { PacketSummary } from "../../types/api";
|
||||
import { Badge } from "../../components/Badge";
|
||||
import { payloadTypeVariant } from "../../components/badge-utils";
|
||||
import { ObservationCard } from "./ObservationCard";
|
||||
|
||||
interface PacketRowProps {
|
||||
packet: PacketSummary;
|
||||
expanded: boolean;
|
||||
detail?: PacketDetail;
|
||||
isFresh?: boolean;
|
||||
onToggle: () => void;
|
||||
selectedObservationId?: number | null;
|
||||
onSelectObservation?: (id: number) => void;
|
||||
}
|
||||
|
||||
// expandable packet card with observations
|
||||
// selectable packet card; observations live in the analyzer drawer
|
||||
|
||||
export function PacketRow({ packet, expanded, detail, isFresh, onToggle, selectedObservationId, onSelectObservation }: PacketRowProps) {
|
||||
export function PacketRow({ packet, expanded, isFresh, onToggle }: PacketRowProps) {
|
||||
return (
|
||||
<div
|
||||
className={`group bg-bg-surface border rounded-md px-3.5 py-2.5 cursor-pointer ${
|
||||
expanded
|
||||
? "border-secondary bg-bg-raised"
|
||||
? "border-primary bg-primary/10"
|
||||
: isFresh
|
||||
? "packet-fresh"
|
||||
: "border-border hover:border-text-dim/30 hover:bg-bg-raised/50"
|
||||
}`}
|
||||
onClick={() => onToggle()}
|
||||
aria-expanded={expanded}
|
||||
aria-pressed={expanded}
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
@@ -37,9 +33,6 @@ export function PacketRow({ packet, expanded, detail, isFresh, onToggle, selecte
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<span className="text-text-muted group-hover:text-text-normal text-[11px] w-3.5 font-mono transition-colors" aria-hidden>
|
||||
{expanded ? "▾" : "▸"}
|
||||
</span>
|
||||
<span className="font-mono text-xs font-semibold text-primary tracking-wider">
|
||||
{formatHex(packet.packetHash)}
|
||||
</span>
|
||||
@@ -58,7 +51,7 @@ export function PacketRow({ packet, expanded, detail, isFresh, onToggle, selecte
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 mt-1 pl-6 text-[11px] text-text-dim">
|
||||
<div className="flex items-center gap-2 mt-1 text-[11px] text-text-dim">
|
||||
<span className="font-mono text-[11px] text-text-muted uppercase tracking-wider bg-text-muted/8 px-1.5 py-px rounded-sm">
|
||||
{packet.routeTypeName || "Unknown"}
|
||||
</span>
|
||||
@@ -75,19 +68,6 @@ export function PacketRow({ packet, expanded, detail, isFresh, onToggle, selecte
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{expanded && detail && (
|
||||
<div className="mt-2 pl-6 flex flex-col gap-1" onClick={(e) => e.stopPropagation()}>
|
||||
{detail.observations.map((obs) => (
|
||||
<ObservationCard
|
||||
key={obs.id}
|
||||
observation={obs}
|
||||
selected={selectedObservationId === obs.id}
|
||||
onClick={onSelectObservation ? () => onSelectObservation(obs.id) : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useRef, useState, useCallback, useEffect, useLayoutEffect } from "react";
|
||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||
import type { PacketSummary, PacketDetail } from "../../types/api";
|
||||
import type { PacketSummary } from "../../types/api";
|
||||
import { PacketRow } from "./PacketRow";
|
||||
import { LIVE_BUFFER_CAP, SCROLL_TOP_THRESHOLD_PX, SCROLL_BOTTOM_THRESHOLD_PX } from "../../lib/constants";
|
||||
|
||||
@@ -12,10 +12,7 @@ interface PacketVirtualListProps {
|
||||
onScrollAwayFromTop: (isAway: boolean) => void;
|
||||
scrollToTopRef?: React.MutableRefObject<(() => void) | null>;
|
||||
expandedHash: string | null;
|
||||
expandedDetail?: PacketDetail;
|
||||
onToggleExpand: (hash: string) => void;
|
||||
selectedObservationId: number | null;
|
||||
onSelectObservation: (id: number) => void;
|
||||
}
|
||||
|
||||
// virtualized scroll list with fresh-item highlighting and infinite load
|
||||
@@ -28,10 +25,7 @@ export function PacketVirtualList({
|
||||
onScrollAwayFromTop,
|
||||
scrollToTopRef,
|
||||
expandedHash,
|
||||
expandedDetail,
|
||||
onToggleExpand,
|
||||
selectedObservationId,
|
||||
onSelectObservation,
|
||||
}: PacketVirtualListProps) {
|
||||
const parentRef = useRef<HTMLDivElement>(null);
|
||||
const knownHashes = useRef<Set<string>>(new Set());
|
||||
@@ -177,11 +171,8 @@ export function PacketVirtualList({
|
||||
<PacketRow
|
||||
packet={packet}
|
||||
expanded={expandedHash === packet.packetHash}
|
||||
detail={expandedHash === packet.packetHash ? expandedDetail : undefined}
|
||||
isFresh={freshHashes.has(packet.packetHash)}
|
||||
onToggle={() => onToggleExpand(packet.packetHash)}
|
||||
selectedObservationId={expandedHash === packet.packetHash ? selectedObservationId : null}
|
||||
onSelectObservation={onSelectObservation}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user