Skip to content

Commit

Permalink
Ensure incident ID is retrieved for each selected row per update
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
clcollins committed Jun 20, 2024
1 parent c4a8dc0 commit 1a873d4
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 30 deletions.
10 changes: 6 additions & 4 deletions pkg/tui/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Expand Down
70 changes: 45 additions & 25 deletions pkg/tui/msgHandlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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):
Expand All @@ -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",
Expand All @@ -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...)
Expand Down Expand Up @@ -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"}
},
Expand Down
16 changes: 15 additions & 1 deletion pkg/tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)))

Expand Down

0 comments on commit 1a873d4

Please sign in to comment.