fix: Implement proper scrolling and sorting for Analysis calls list
This commit is contained in:
@@ -153,6 +153,27 @@ func (s *CallFlowStore) GetRecentFlows(n int) []*CallFlow {
|
|||||||
return flows
|
return flows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSortedFlows returns all call flows sorted by StartTime (oldest first)
|
||||||
|
func (s *CallFlowStore) GetSortedFlows() []*CallFlow {
|
||||||
|
flows := s.GetAllFlows()
|
||||||
|
|
||||||
|
// Sort by start time ascending (oldest first), then by CallID for stable order
|
||||||
|
for i := 0; i < len(flows)-1; i++ {
|
||||||
|
for j := i + 1; j < len(flows); j++ {
|
||||||
|
// Compare by StartTime first
|
||||||
|
if flows[i].StartTime.After(flows[j].StartTime) {
|
||||||
|
flows[i], flows[j] = flows[j], flows[i]
|
||||||
|
} else if flows[i].StartTime.Equal(flows[j].StartTime) {
|
||||||
|
// If same time, sort by CallID for stable order
|
||||||
|
if flows[i].CallID > flows[j].CallID {
|
||||||
|
flows[i], flows[j] = flows[j], flows[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flows
|
||||||
|
}
|
||||||
|
|
||||||
// Count returns the number of call flows
|
// Count returns the number of call flows
|
||||||
func (s *CallFlowStore) Count() int {
|
func (s *CallFlowStore) Count() int {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ type Model struct {
|
|||||||
callFlowStore *sip.CallFlowStore
|
callFlowStore *sip.CallFlowStore
|
||||||
|
|
||||||
// Call flow analysis
|
// Call flow analysis
|
||||||
|
analysisOffset int // For pagination in analysis view
|
||||||
selectedFlow int
|
selectedFlow int
|
||||||
selectedPacketIndex int
|
selectedPacketIndex int
|
||||||
flowList list.Model
|
flowList list.Model
|
||||||
@@ -484,11 +485,51 @@ func (m *Model) handleViewKeys(msg tea.KeyMsg) tea.Cmd {
|
|||||||
case "up", "k":
|
case "up", "k":
|
||||||
if m.selectedFlow > 0 {
|
if m.selectedFlow > 0 {
|
||||||
m.selectedFlow--
|
m.selectedFlow--
|
||||||
|
if m.selectedFlow < m.analysisOffset {
|
||||||
|
m.analysisOffset = m.selectedFlow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case "down", "j":
|
case "down", "j":
|
||||||
flows := m.callFlowStore.GetRecentFlows(20)
|
flows := m.callFlowStore.GetSortedFlows()
|
||||||
if m.selectedFlow < len(flows)-1 {
|
if m.selectedFlow < len(flows)-1 {
|
||||||
m.selectedFlow++
|
m.selectedFlow++
|
||||||
|
// Check if we need to scroll down
|
||||||
|
// Visible height calculation (approximate)
|
||||||
|
headerHeight := 4 // Title + Call count + padding
|
||||||
|
footerHeight := 2 // Help text
|
||||||
|
availableHeight := m.height - headerHeight - footerHeight
|
||||||
|
if availableHeight < 5 {
|
||||||
|
availableHeight = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.selectedFlow >= m.analysisOffset+availableHeight {
|
||||||
|
m.analysisOffset++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "pgup":
|
||||||
|
m.selectedFlow -= 10
|
||||||
|
if m.selectedFlow < 0 {
|
||||||
|
m.selectedFlow = 0
|
||||||
|
}
|
||||||
|
if m.selectedFlow < m.analysisOffset {
|
||||||
|
m.analysisOffset = m.selectedFlow
|
||||||
|
}
|
||||||
|
case "pgdown":
|
||||||
|
flows := m.callFlowStore.GetSortedFlows()
|
||||||
|
m.selectedFlow += 10
|
||||||
|
if m.selectedFlow >= len(flows) {
|
||||||
|
m.selectedFlow = len(flows) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
headerHeight := 4
|
||||||
|
footerHeight := 2
|
||||||
|
availableHeight := m.height - headerHeight - footerHeight
|
||||||
|
if availableHeight < 5 {
|
||||||
|
availableHeight = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.selectedFlow >= m.analysisOffset+availableHeight {
|
||||||
|
m.analysisOffset = m.selectedFlow - availableHeight + 1
|
||||||
}
|
}
|
||||||
case "enter":
|
case "enter":
|
||||||
m.subView = SubViewCallDetail
|
m.subView = SubViewCallDetail
|
||||||
@@ -1209,7 +1250,7 @@ func (m Model) viewCapture() string {
|
|||||||
func (m Model) viewAnalysis() string {
|
func (m Model) viewAnalysis() string {
|
||||||
title := m.styles.Title.Render("📊 Analysis")
|
title := m.styles.Title.Render("📊 Analysis")
|
||||||
|
|
||||||
flows := m.callFlowStore.GetRecentFlows(20)
|
flows := m.callFlowStore.GetSortedFlows()
|
||||||
|
|
||||||
if len(flows) == 0 {
|
if len(flows) == 0 {
|
||||||
return lipgloss.JoinVertical(lipgloss.Left, title,
|
return lipgloss.JoinVertical(lipgloss.Left, title,
|
||||||
@@ -1221,8 +1262,9 @@ func (m Model) viewAnalysis() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var lines []string
|
var lines []string
|
||||||
lines = append(lines, fmt.Sprintf("Calls: %d", len(flows)))
|
// Header is handled in return statement now
|
||||||
lines = append(lines, "")
|
// lines = append(lines, fmt.Sprintf("Calls: %d", len(flows)))
|
||||||
|
// lines = append(lines, "")
|
||||||
|
|
||||||
for i, flow := range flows {
|
for i, flow := range flows {
|
||||||
prefix := " "
|
prefix := " "
|
||||||
@@ -1253,10 +1295,35 @@ func (m Model) viewAnalysis() string {
|
|||||||
lines = append(lines, style.Render(summary))
|
lines = append(lines, style.Render(summary))
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = append(lines, "")
|
// Apply Viewport/Scrolling
|
||||||
lines = append(lines, m.styles.Help.Render("↑/↓ select • Enter details • q quit"))
|
headerHeight := 4 // Title + Call count + padding
|
||||||
|
footerHeight := 2 // Help text
|
||||||
|
availableHeight := m.height - headerHeight - footerHeight
|
||||||
|
if availableHeight < 5 {
|
||||||
|
availableHeight = 5
|
||||||
|
}
|
||||||
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Left, title, lipgloss.JoinVertical(lipgloss.Left, lines...))
|
// Sanity check offset
|
||||||
|
if m.analysisOffset > len(lines)-1 {
|
||||||
|
m.analysisOffset = len(lines) - 1
|
||||||
|
}
|
||||||
|
if m.analysisOffset < 0 {
|
||||||
|
m.analysisOffset = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
end := m.analysisOffset + availableHeight
|
||||||
|
if end > len(lines) {
|
||||||
|
end = len(lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
visibleLines := lines[m.analysisOffset:end]
|
||||||
|
|
||||||
|
return lipgloss.JoinVertical(lipgloss.Left, title,
|
||||||
|
fmt.Sprintf("Calls: %d (Showing %d-%d)", len(flows), m.analysisOffset+1, end),
|
||||||
|
"",
|
||||||
|
lipgloss.JoinVertical(lipgloss.Left, visibleLines...),
|
||||||
|
"",
|
||||||
|
m.styles.Help.Render("↑/↓ select • Enter details • q quit"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) viewNetworkMap() string {
|
func (m Model) viewNetworkMap() string {
|
||||||
|
|||||||
Reference in New Issue
Block a user