From 1a873d46869e09e992d198133d62579dfcdbf622 Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Thu, 6 Jun 2024 11:56:49 -1000 Subject: [PATCH] Ensure incident ID is retrieved for each selected row per update This adds a call to get the incident id of the incident represented in the selcted row each time the table view is updated, and this ID is used to select the incident with getSelectedIncident(). This ensures that the currently highlighted row is always the incident that is acted upon in table view, rather than a previously selected incident. When no row is selected, attempts to act on an incident return a status message to that effect, rather than panicing. Fixes #44 Fixes #52 Signed-off-by: Chris Collins --- pkg/tui/commands.go | 10 +++--- pkg/tui/msgHandlers.go | 70 +++++++++++++++++++++++++++--------------- pkg/tui/tui.go | 16 +++++++++- 3 files changed, 66 insertions(+), 30 deletions(-) diff --git a/pkg/tui/commands.go b/pkg/tui/commands.go index b39e3ca..4d72462 100644 --- a/pkg/tui/commands.go +++ b/pkg/tui/commands.go @@ -106,6 +106,11 @@ type gotIncidentMsg struct { } func getIncident(p *pd.Config, id string) tea.Cmd { + if id == "" { + return func() tea.Msg { + return setStatusMsg{"No incident selected"} + } + } return func() tea.Msg { ctx := context.Background() i, err := p.Client.GetIncidentWithContext(ctx, id) @@ -425,11 +430,8 @@ func acknowledged(a []pagerduty.Acknowledgement) string { func doIfIncidentSelected(m *model, cmd tea.Cmd) tea.Cmd { if m.table.SelectedRow() == nil { log.Debug("doIfIncidentSelected", "selectedRow", "nil") - m.setStatus(nilIncidentErr) m.viewingIncident = false - return tea.Sequence( - func() tea.Msg { return errMsg{errors.New(nilIncidentErr)} }, - ) + return func() tea.Msg { return setStatusMsg{nilIncidentMsg} } } log.Debug("doIfIncidentSelected", "selectedRow", m.table.SelectedRow()) return tea.Sequence( diff --git a/pkg/tui/msgHandlers.go b/pkg/tui/msgHandlers.go index 8a78d63..dab0762 100644 --- a/pkg/tui/msgHandlers.go +++ b/pkg/tui/msgHandlers.go @@ -10,6 +10,12 @@ import ( "github.com/charmbracelet/log" ) +// setStatusMsgHandler is the message handler for the setStatusMsg message +func (m model) setStatusMsgHandler(msg tea.Msg) (tea.Model, tea.Cmd) { + m.setStatus(msg.(setStatusMsg).Status()) + return m, nil +} + // errMsgHandler is the message handler for the errMsg message func (m model) errMsgHandler(msg tea.Msg) (tea.Model, tea.Cmd) { log.Error("errMsgHandler", "tea.errMsg", msg) @@ -72,6 +78,18 @@ func switchTableFocusMode(m model, msg tea.Msg) (tea.Model, tea.Cmd) { log.Debug("switchTableFocusMode", reflect.TypeOf(msg), msg) var cmds []tea.Cmd + // [1] is column two of the row: the incident ID + var row table.Row + var incidentID string + row = m.table.SelectedRow() + if row == nil { + incidentID = "" + } else { + incidentID = m.table.SelectedRow()[1] + } + + log.Debug("switchTableFocusMode", "incidentID", incidentID) + switch msg := msg.(type) { case tea.KeyMsg: switch { @@ -90,11 +108,9 @@ func switchTableFocusMode(m model, msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, defaultKeyMap.Bottom): m.table.GotoBottom() - case key.Matches(msg, defaultKeyMap.Enter): - m.viewingIncident = true - return m, doIfIncidentSelected(&m, func() tea.Msg { - return waitForSelectedIncidentThenDoMsg{action: func() tea.Msg { return renderIncidentMsg("render") }, msg: "render"} - }, + case key.Matches(msg, defaultKeyMap.Input): + return m, tea.Sequence( + m.input.Focus(), ) case key.Matches(msg, defaultKeyMap.Team): @@ -110,9 +126,17 @@ func switchTableFocusMode(m model, msg tea.Msg) (tea.Model, tea.Cmd) { // and then can be acted upon. Since tea.Sequence does not wait for completion, the // "waitForSelectedIncidentsThen..." functions are used to wait for the selected incident // to be retrieved from PagerDuty + case key.Matches(msg, defaultKeyMap.Enter): + m.viewingIncident = true + return m, doIfIncidentSelected(&m, func() tea.Msg { + return waitForSelectedIncidentThenDoMsg{ + action: func() tea.Msg { return renderIncidentMsg("render") }, msg: "render", + } + }) + case key.Matches(msg, defaultKeyMap.Silence): - return m, tea.Sequence( - func() tea.Msg { return getIncidentMsg(m.table.SelectedRow()[1]) }, + return m, doIfIncidentSelected(&m, tea.Sequence( + func() tea.Msg { return getIncidentMsg(incidentID) }, func() tea.Msg { return waitForSelectedIncidentThenDoMsg{ msg: "silence", @@ -121,42 +145,38 @@ func switchTableFocusMode(m model, msg tea.Msg) (tea.Model, tea.Cmd) { }, } }, - ) + )) case key.Matches(msg, defaultKeyMap.Ack): - return m, tea.Sequence( - func() tea.Msg { return getIncidentMsg(m.table.SelectedRow()[1]) }, + return m, doIfIncidentSelected(&m, tea.Sequence( + func() tea.Msg { return getIncidentMsg(incidentID) }, func() tea.Msg { return waitForSelectedIncidentsThenAcknowledgeMsg("wait") }, - ) + )) case key.Matches(msg, defaultKeyMap.Note): - return m, tea.Sequence( - func() tea.Msg { return getIncidentMsg(m.table.SelectedRow()[1]) }, + return m, doIfIncidentSelected(&m, tea.Sequence( + func() tea.Msg { return getIncidentMsg(incidentID) }, func() tea.Msg { return waitForSelectedIncidentThenDoMsg{action: openEditorCmd(m.editor), msg: "add note"} }, - ) + )) case key.Matches(msg, defaultKeyMap.Login): - return m, tea.Sequence( - func() tea.Msg { return getIncidentMsg(m.table.SelectedRow()[1]) }, + return m, doIfIncidentSelected(&m, tea.Sequence( + func() tea.Msg { return getIncidentMsg(incidentID) }, func() tea.Msg { return waitForSelectedIncidentThenDoMsg{action: func() tea.Msg { return loginMsg("login") }, msg: "wait"} }, - ) + )) case key.Matches(msg, defaultKeyMap.Open): - return m, tea.Sequence( - func() tea.Msg { return getIncidentMsg(m.table.SelectedRow()[1]) }, + return m, doIfIncidentSelected(&m, tea.Sequence( + func() tea.Msg { return getIncidentMsg(incidentID) }, func() tea.Msg { return waitForSelectedIncidentThenDoMsg{action: func() tea.Msg { return openBrowserMsg("incident") }, msg: ""} }, - ) + )) - case key.Matches(msg, defaultKeyMap.Input): - return m, tea.Sequence( - m.input.Focus(), - ) } } return m, tea.Batch(cmds...) @@ -217,7 +237,7 @@ func switchIncidentFocusMode(m model, msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, defaultKeyMap.Login): return m, tea.Sequence( - func() tea.Msg { return getIncidentMsg(m.table.SelectedRow()[1]) }, + func() tea.Msg { return getIncidentMsg(m.selectedIncident.ID) }, func() tea.Msg { return waitForSelectedIncidentThenDoMsg{action: func() tea.Msg { return loginMsg("login") }, msg: "wait"} }, diff --git a/pkg/tui/tui.go b/pkg/tui/tui.go index ed3de4e..e3bd215 100644 --- a/pkg/tui/tui.go +++ b/pkg/tui/tui.go @@ -16,10 +16,15 @@ const ( defaultInputPrompt = " $ " u nilNoteErr = "incident note content is empty" - nilIncidentErr = "no incident selected" + nilIncidentMsg = "no incident selected" ) type errMsg struct{ error } +type setStatusMsg struct{ string } + +func (s setStatusMsg) Status() string { + return s.string +} func (m model) Init() tea.Cmd { if m.err != nil { @@ -80,8 +85,17 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: return m.keyMsgHandler(msg) + case setStatusMsg: + return m.setStatusMsgHandler(msg) + // Command to get an incident by ID case getIncidentMsg: + if msg == "" { + return m, func() tea.Msg { + return setStatusMsg{"no incident selected"} + } + } + m.setStatus(fmt.Sprintf("getting details for incident %v...", msg)) cmds = append(cmds, getIncident(m.config, string(msg)))