fix: Refactor Update loop to ensure packets are processed in all views

This commit is contained in:
Jose Luis Montañes Ojados
2026-01-19 16:25:32 +01:00
parent 1344b730af
commit 751bf380d7

View File

@@ -299,11 +299,61 @@ type ErrorMsg struct {
// Update handles messages and updates the model // Update handles messages and updates the model
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd var cmds []tea.Cmd
// Handle subview updates first // GLOBAL HANDLERS: Handle signals independent of view
switch msg := msg.(type) {
case PacketMsg:
if msg.Packet != nil {
m.packetCount++
summary := formatPacketSummary(msg.Packet, m.networkMap)
m.lastPackets = append(m.lastPackets, summary)
if len(m.lastPackets) > 50 {
m.lastPackets = m.lastPackets[1:]
}
m.callFlowStore.AddPacket(msg.Packet)
// If we are in Call Detail view, we might need to update the viewport content dynamically!
if m.subView == SubViewCallDetail {
// Re-render subview content effectively updates the strings, but
// we need to set the content on viewport again if it changed.
// This is handled in View() normally, but viewport needs SetContent.
// Let's force a viewport update by triggering a dummy message or just re-setting it.
// Actually, View() calls renderCallDetail which calls SetContent.
// But View() is only called if Update returns a modified model.
// We modified the store, so that counts.
}
}
// PROCESS NEXT PACKET - CRITICAL loop
if m.capturing {
cmds = append(cmds, waitForPacket(m.packetChan))
}
// If we processed a packet, we typically don't need to pass this msg to subviews
// UNLESS the subview reacts to it explicitly.
// For now, we return here to avoid double processing, BUT we must ensure
// UI refreshes.
return m, tea.Batch(cmds...)
case ErrorMsg:
m.captureError = msg.Error.Error()
return m, nil
}
// Handle standard key/window messages dependent on view
// Handle subview updates
if m.subView != SubViewNone { if m.subView != SubViewNone {
return m.updateSubView(msg) newM, cmd := m.updateSubView(msg)
// We need to type assert back to Model because updateSubView follows the interface but returns concrete logic
// Actually updateSubView returns tea.Model, tea.Cmd.
// Since we are inside Model.Update, we can cast or just return.
realM, ok := newM.(Model)
if ok {
m = realM
}
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
} }
switch msg := msg.(type) { switch msg := msg.(type) {
@@ -321,7 +371,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "4": case "4":
m.currentView = ViewNetworkMap m.currentView = ViewNetworkMap
default: default:
cmd = m.handleViewKeys(msg) cmds = append(cmds, m.handleViewKeys(msg))
} }
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
@@ -342,28 +392,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.viewport.Width = m.width / 2 // Right pane width m.viewport.Width = m.width / 2 // Right pane width
m.viewport.Height = contentHeight m.viewport.Height = contentHeight
case PacketMsg:
if msg.Packet != nil {
m.packetCount++
summary := formatPacketSummary(msg.Packet, m.networkMap)
m.lastPackets = append(m.lastPackets, summary)
if len(m.lastPackets) > 50 {
m.lastPackets = m.lastPackets[1:]
}
m.callFlowStore.AddPacket(msg.Packet)
}
// Continue waiting if we are still capturing
if m.capturing {
return m, waitForPacket(m.packetChan)
}
case ErrorMsg:
m.captureError = msg.Error.Error()
} }
return m, cmd return m, tea.Batch(cmds...)
} }
func (m *Model) handleViewKeys(msg tea.KeyMsg) tea.Cmd { func (m *Model) handleViewKeys(msg tea.KeyMsg) tea.Cmd {