adding another tui window for closed issues; adding scrolling for the issue display windows
This commit is contained in:
@@ -31,8 +31,10 @@ func (h HelpBar) View() string {
|
|||||||
|
|
||||||
func (h HelpBar) shortHelp() string {
|
func (h HelpBar) shortHelp() string {
|
||||||
keys := []string{
|
keys := []string{
|
||||||
|
styles.HighlightKey("tab") + " switch",
|
||||||
styles.HighlightKey("↑/k") + " up",
|
styles.HighlightKey("↑/k") + " up",
|
||||||
styles.HighlightKey("↓/j") + " down",
|
styles.HighlightKey("↓/j") + " down",
|
||||||
|
styles.HighlightKey("pgup/pgdn") + " page",
|
||||||
styles.HighlightKey("a") + " add",
|
styles.HighlightKey("a") + " add",
|
||||||
styles.HighlightKey("e/d/s") + " edit",
|
styles.HighlightKey("e/d/s") + " edit",
|
||||||
styles.HighlightKey("x") + " delete",
|
styles.HighlightKey("x") + " delete",
|
||||||
@@ -68,12 +70,13 @@ func (h HelpBar) fullHelp() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rows := []string{
|
rows := []string{
|
||||||
renderRow("↑/k", "up", "enter", "view issue"),
|
renderRow("tab", "switch window", "↑/k", "up"),
|
||||||
renderRow("↓/j", "down", "b", "back to list"),
|
renderRow("enter", "view issue", "↓/j", "down"),
|
||||||
renderRow("a", "add issue", "e", "edit title"),
|
renderRow("pgup", "page up", "pgdn", "page down"),
|
||||||
renderRow("d", "edit desc", "s", "change status"),
|
renderRow("b", "back to list", "a", "add issue"),
|
||||||
renderRow("x", "delete issue", "?", "help"),
|
renderRow("e", "edit title", "d", "edit desc"),
|
||||||
renderRow("q", "quit", "", ""),
|
renderRow("s", "change status", "x", "delete issue"),
|
||||||
|
renderRow("?", "help", "q", "quit"),
|
||||||
}
|
}
|
||||||
content := lipgloss.JoinVertical(lipgloss.Left, rows...)
|
content := lipgloss.JoinVertical(lipgloss.Left, rows...)
|
||||||
return lipgloss.NewStyle().
|
return lipgloss.NewStyle().
|
||||||
|
|||||||
@@ -108,6 +108,51 @@ func NewIssueList(svc *service.Services, width, height int) IssueList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewIssueListFromIssues(svc *service.Services, issues []models.Issue, width, height int) IssueList {
|
||||||
|
// for making an IssueList from a pre-existing list of issues.
|
||||||
|
listIssues := make([]list.Item, len(issues))
|
||||||
|
for i, issue := range issues {
|
||||||
|
listIssues[i] = ListIssue{Issue: issue}
|
||||||
|
}
|
||||||
|
l := list.New(listIssues, NewIssueListDelegate(width), width, height)
|
||||||
|
l.SetShowTitle(false)
|
||||||
|
l.SetShowHelp(false)
|
||||||
|
l.SetShowStatusBar(false)
|
||||||
|
l.SetFilteringEnabled(true)
|
||||||
|
l.FilterInput.PromptStyle = styles.FilterPromptStyle
|
||||||
|
l.FilterInput.Cursor.Style = styles.FilterStyle
|
||||||
|
l.FilterInput.TextStyle = styles.FilterInputStyle
|
||||||
|
l.FilterInput.Prompt = "🔍 "
|
||||||
|
return IssueList{
|
||||||
|
list: l,
|
||||||
|
svc: svc,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenAndInProgressOnly(issues []models.Issue) []models.Issue {
|
||||||
|
// used for the first window in the dashboard
|
||||||
|
out := make([]models.Issue, 0, len(issues))
|
||||||
|
for _, issue := range issues {
|
||||||
|
if issue.Status == models.StatusOpen || issue.Status == models.StatusInProgress {
|
||||||
|
out = append(out, issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClosedOnly(issues []models.Issue) []models.Issue {
|
||||||
|
// used for the second window in the dashboard
|
||||||
|
out := make([]models.Issue, 0, len(issues))
|
||||||
|
for _, issue := range issues {
|
||||||
|
if issue.Status == models.StatusClosed {
|
||||||
|
out = append(out, issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func (l *IssueList) Update(msg tea.Msg) (tea.Cmd, bool) {
|
func (l *IssueList) Update(msg tea.Msg) (tea.Cmd, bool) {
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
oldIndex := l.list.Index()
|
oldIndex := l.list.Index()
|
||||||
@@ -160,9 +205,7 @@ func (l IssueList) renderFilteredItems() string {
|
|||||||
visibleItems = l.list.Items()
|
visibleItems = l.list.Items()
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsPerPage := l.list.Paginator.ItemsOnPage(len(visibleItems))
|
start, end := l.list.Paginator.GetSliceBounds(len(visibleItems))
|
||||||
start := l.list.Paginator.Page * itemsPerPage
|
|
||||||
end := min(start+itemsPerPage, len(visibleItems))
|
|
||||||
|
|
||||||
cursor := l.list.Index()
|
cursor := l.list.Index()
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DashboardKeyMap struct {
|
type DashboardKeyMap struct {
|
||||||
Help key.Binding
|
Help key.Binding
|
||||||
Quit key.Binding
|
Quit key.Binding
|
||||||
SelectIssue key.Binding
|
SelectIssue key.Binding
|
||||||
BackToList key.Binding
|
BackToList key.Binding
|
||||||
ScrollUp key.Binding
|
ScrollUp key.Binding
|
||||||
ScrollDown key.Binding
|
ScrollDown key.Binding
|
||||||
|
SwitchWindow key.Binding
|
||||||
EditTitle key.Binding
|
EditTitle key.Binding
|
||||||
EditDescription key.Binding
|
EditDescription key.Binding
|
||||||
ChangeStatus key.Binding
|
ChangeStatus key.Binding
|
||||||
@@ -44,6 +45,10 @@ var defaultDashboardKeyMap = DashboardKeyMap{
|
|||||||
key.WithKeys("down", "j"),
|
key.WithKeys("down", "j"),
|
||||||
key.WithHelp("↓/j", "down"),
|
key.WithHelp("↓/j", "down"),
|
||||||
),
|
),
|
||||||
|
SwitchWindow: key.NewBinding(
|
||||||
|
key.WithKeys("tab"),
|
||||||
|
key.WithHelp("tab", "switch window"),
|
||||||
|
),
|
||||||
EditTitle: key.NewBinding(
|
EditTitle: key.NewBinding(
|
||||||
key.WithKeys("e"),
|
key.WithKeys("e"),
|
||||||
key.WithHelp("e", "edit title"),
|
key.WithHelp("e", "edit title"),
|
||||||
@@ -74,6 +79,8 @@ func (d *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd {
|
|||||||
d.helpBar.ToggleHelp()
|
d.helpBar.ToggleHelp()
|
||||||
case key.Matches(msg, d.keyMap.Quit):
|
case key.Matches(msg, d.keyMap.Quit):
|
||||||
return tea.Quit
|
return tea.Quit
|
||||||
|
case key.Matches(msg, d.keyMap.SwitchWindow):
|
||||||
|
d.ToggleFocusedWindow()
|
||||||
case d.IsFocusedOnList() && key.Matches(msg, d.keyMap.SelectIssue):
|
case d.IsFocusedOnList() && key.Matches(msg, d.keyMap.SelectIssue):
|
||||||
d.FocusDetail()
|
d.FocusDetail()
|
||||||
case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.BackToList):
|
case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.BackToList):
|
||||||
@@ -83,25 +90,26 @@ func (d *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd {
|
|||||||
case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.ScrollDown):
|
case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.ScrollDown):
|
||||||
d.issueDetail.ScrollDown(1)
|
d.issueDetail.ScrollDown(1)
|
||||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && key.Matches(msg, d.keyMap.EditTitle):
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && key.Matches(msg, d.keyMap.EditTitle):
|
||||||
if selected := d.issueList.SelectedItem(); selected.ID != "" {
|
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||||
d.startEditTitle(selected)
|
d.startEditTitle(selected)
|
||||||
cmd = d.titleInput.Focus()
|
cmd = d.titleInput.Focus()
|
||||||
}
|
}
|
||||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && key.Matches(msg, d.keyMap.EditDescription):
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && key.Matches(msg, d.keyMap.EditDescription):
|
||||||
if selected := d.issueList.SelectedItem(); selected.ID != "" {
|
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||||
d.startEditDescription(selected)
|
d.startEditDescription(selected)
|
||||||
cmd = d.descriptionInput.Focus()
|
cmd = d.descriptionInput.Focus()
|
||||||
}
|
}
|
||||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && key.Matches(msg, d.keyMap.ChangeStatus):
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && key.Matches(msg, d.keyMap.ChangeStatus):
|
||||||
if selected := d.issueList.SelectedItem(); selected.ID != "" {
|
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||||
d.startChooseStatus(selected)
|
d.startChooseStatus(selected)
|
||||||
}
|
}
|
||||||
case !d.editingTitle && !d.creatingIssue && key.Matches(msg, d.keyMap.AddIssue):
|
case !d.editingTitle && !d.creatingIssue && key.Matches(msg, d.keyMap.AddIssue):
|
||||||
d.startCreateIssue()
|
d.startCreateIssue()
|
||||||
cmd = d.createTitleInput.Focus()
|
cmd = d.createTitleInput.Focus()
|
||||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.confirmingDelete && key.Matches(msg, d.keyMap.DeleteIssue):
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.confirmingDelete && key.Matches(msg, d.keyMap.DeleteIssue):
|
||||||
if selected := d.issueList.SelectedItem(); selected.ID != "" {
|
fl := d.FocusedIssueList()
|
||||||
d.startConfirmDelete(selected.ID, d.issueList.Index())
|
if selected := fl.SelectedItem(); selected.ID != "" {
|
||||||
|
d.startConfirmDelete(selected.ID, fl.Index())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package dashboard
|
package dashboard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
"github.com/charmbracelet/bubbles/textarea"
|
"github.com/charmbracelet/bubbles/textarea"
|
||||||
@@ -8,15 +10,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
header Header
|
header Header
|
||||||
issueList IssueList
|
issueList IssueList
|
||||||
issueDetail IssueDetail
|
issueDetail IssueDetail
|
||||||
helpBar HelpBar
|
closedIssueList IssueList
|
||||||
keyMap DashboardKeyMap
|
helpBar HelpBar
|
||||||
svc *service.Services
|
keyMap DashboardKeyMap
|
||||||
width int
|
svc *service.Services
|
||||||
height int
|
width int
|
||||||
focusedPane int // 0 = list, 1 = detail
|
height int
|
||||||
|
focusedWindow int // 0 = main (display issues), 1 = closed issues
|
||||||
|
focusedPaneMain int // 0 = list, 1 = detail
|
||||||
|
focusedPaneClosed int
|
||||||
|
|
||||||
editingTitle bool // true while we are editing a title
|
editingTitle bool // true while we are editing a title
|
||||||
titleInput textinput.Model
|
titleInput textinput.Model
|
||||||
@@ -38,16 +43,20 @@ type Model struct {
|
|||||||
|
|
||||||
func NewDashboard(svc *service.Services) *Model {
|
func NewDashboard(svc *service.Services) *Model {
|
||||||
m := &Model{
|
m := &Model{
|
||||||
header: NewHeader("Project Manager Dashboard"),
|
header: NewHeader("Project Manager Dashboard"),
|
||||||
keyMap: defaultDashboardKeyMap,
|
keyMap: defaultDashboardKeyMap,
|
||||||
svc: svc,
|
svc: svc,
|
||||||
width: 80,
|
width: 80,
|
||||||
height: 24,
|
height: 24,
|
||||||
focusedPane: 0,
|
focusedWindow: 0,
|
||||||
|
focusedPaneMain: 0,
|
||||||
|
focusedPaneClosed: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
m.issueList = NewIssueList(svc, 0, 0)
|
allIssues, _ := svc.Beads.AllIssues(context.Background())
|
||||||
|
m.issueList = NewIssueListFromIssues(svc, OpenAndInProgressOnly(allIssues), 0, 0)
|
||||||
m.issueDetail = NewIssueDetail()
|
m.issueDetail = NewIssueDetail()
|
||||||
|
m.closedIssueList = NewIssueListFromIssues(svc, ClosedOnly(allIssues), 0, 0)
|
||||||
m.helpBar = NewHelpBar(m.keyMap)
|
m.helpBar = NewHelpBar(m.keyMap)
|
||||||
|
|
||||||
ti := textinput.New()
|
ti := textinput.New()
|
||||||
@@ -68,6 +77,8 @@ func NewDashboard(svc *service.Services) *Model {
|
|||||||
|
|
||||||
if selected := m.issueList.SelectedItem(); selected.ID != "" {
|
if selected := m.issueList.SelectedItem(); selected.ID != "" {
|
||||||
m.issueDetail.SetIssue(selected.Issue)
|
m.issueDetail.SetIssue(selected.Issue)
|
||||||
|
} else if selected := m.closedIssueList.SelectedItem(); selected.ID != "" {
|
||||||
|
m.issueDetail.SetIssue(selected.Issue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return m
|
return m
|
||||||
@@ -109,27 +120,64 @@ func (m *Model) Init() tea.Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) IsFocusedOnList() bool {
|
func (m *Model) IsFocusedOnList() bool {
|
||||||
return m.focusedPane == 0
|
if m.focusedWindow == 0 {
|
||||||
|
return m.focusedPaneMain == 0
|
||||||
|
}
|
||||||
|
return m.focusedPaneClosed == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) IsFocusedOnDetail() bool {
|
func (m *Model) IsFocusedOnDetail() bool {
|
||||||
return m.focusedPane == 1
|
if m.focusedWindow == 0 {
|
||||||
|
return m.focusedPaneMain == 1
|
||||||
|
}
|
||||||
|
return m.focusedPaneClosed == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) FocusList() {
|
func (m *Model) FocusList() {
|
||||||
m.focusedPane = 0
|
if m.focusedWindow == 0 {
|
||||||
|
m.focusedPaneMain = 0
|
||||||
|
} else {
|
||||||
|
m.focusedPaneClosed = 0
|
||||||
|
}
|
||||||
m.issueDetail.SetFocused(false)
|
m.issueDetail.SetFocused(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) FocusDetail() {
|
func (m *Model) FocusDetail() {
|
||||||
m.focusedPane = 1
|
if m.focusedWindow == 0 {
|
||||||
|
m.focusedPaneMain = 1
|
||||||
|
} else {
|
||||||
|
m.focusedPaneClosed = 1
|
||||||
|
}
|
||||||
m.issueDetail.SetFocused(true)
|
m.issueDetail.SetFocused(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ToggleFocus() {
|
func (m *Model) ToggleFocus() {
|
||||||
if m.focusedPane == 0 {
|
if m.IsFocusedOnList() {
|
||||||
m.FocusDetail()
|
m.FocusDetail()
|
||||||
} else {
|
} else {
|
||||||
m.FocusList()
|
m.FocusList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FocusedIssueList returns the issue list of the currently focused window.
|
||||||
|
func (m *Model) FocusedIssueList() *IssueList {
|
||||||
|
if m.focusedWindow == 0 {
|
||||||
|
return &m.issueList
|
||||||
|
}
|
||||||
|
return &m.closedIssueList
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToggleFocusedWindow switches focus between main and closed issues window.
|
||||||
|
func (m *Model) ToggleFocusedWindow() {
|
||||||
|
m.focusedWindow = 1 - m.focusedWindow
|
||||||
|
if m.focusedWindow == 0 {
|
||||||
|
if selected := m.issueList.SelectedItem(); selected.ID != "" {
|
||||||
|
m.issueDetail.SetIssue(selected.Issue)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if selected := m.closedIssueList.SelectedItem(); selected.ID != "" {
|
||||||
|
m.issueDetail.SetIssue(selected.Issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.issueDetail.SetFocused(m.IsFocusedOnDetail())
|
||||||
|
}
|
||||||
|
|||||||
@@ -95,14 +95,15 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
setItemsCmd := m.issueList.SetIssues(issues)
|
setItemsCmd := m.issueList.SetIssues(OpenAndInProgressOnly(issues))
|
||||||
|
closedSetCmd := m.closedIssueList.SetIssues(ClosedOnly(issues))
|
||||||
for _, issue := range issues {
|
for _, issue := range issues {
|
||||||
if issue.ID == msg.IssueID {
|
if issue.ID == msg.IssueID {
|
||||||
m.issueDetail.SetIssue(issue)
|
m.issueDetail.SetIssue(issue)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m, tea.Sequence(setItemsCmd, func() tea.Msg { return selectIssueMsg{IssueID: msg.IssueID} })
|
return m, tea.Sequence(setItemsCmd, closedSetCmd, func() tea.Msg { return selectIssueMsg{IssueID: msg.IssueID} })
|
||||||
|
|
||||||
case issueDescriptionUpdatedMsg:
|
case issueDescriptionUpdatedMsg:
|
||||||
m.editingDescription = false
|
m.editingDescription = false
|
||||||
@@ -115,15 +116,15 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
setItemsCmd := m.issueList.SetIssues(issues)
|
setItemsCmd := m.issueList.SetIssues(OpenAndInProgressOnly(issues))
|
||||||
|
closedSetCmd := m.closedIssueList.SetIssues(ClosedOnly(issues))
|
||||||
for _, issue := range issues {
|
for _, issue := range issues {
|
||||||
if issue.ID == msg.IssueID {
|
if issue.ID == msg.IssueID {
|
||||||
m.issueDetail.SetIssue(issue)
|
m.issueDetail.SetIssue(issue)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// after list is updated, select the edited issue again
|
return m, tea.Sequence(setItemsCmd, closedSetCmd, func() tea.Msg { return selectIssueMsg{IssueID: msg.IssueID} })
|
||||||
return m, tea.Sequence(setItemsCmd, func() tea.Msg { return selectIssueMsg{IssueID: msg.IssueID} })
|
|
||||||
|
|
||||||
case issueStatusUpdatedMsg:
|
case issueStatusUpdatedMsg:
|
||||||
m.choosingStatus = false
|
m.choosingStatus = false
|
||||||
@@ -135,17 +136,19 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
setItemsCmd := m.issueList.SetIssues(issues)
|
setItemsCmd := m.issueList.SetIssues(OpenAndInProgressOnly(issues))
|
||||||
|
closedSetCmd := m.closedIssueList.SetIssues(ClosedOnly(issues))
|
||||||
for _, issue := range issues {
|
for _, issue := range issues {
|
||||||
if issue.ID == msg.IssueID {
|
if issue.ID == msg.IssueID {
|
||||||
m.issueDetail.SetIssue(issue)
|
m.issueDetail.SetIssue(issue)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m, tea.Sequence(setItemsCmd, func() tea.Msg { return selectIssueMsg{IssueID: msg.IssueID} })
|
return m, tea.Sequence(setItemsCmd, closedSetCmd, func() tea.Msg { return selectIssueMsg{IssueID: msg.IssueID} })
|
||||||
|
|
||||||
case selectIssueMsg:
|
case selectIssueMsg:
|
||||||
m.issueList.SelectIssueID(msg.IssueID)
|
m.issueList.SelectIssueID(msg.IssueID)
|
||||||
|
m.closedIssueList.SelectIssueID(msg.IssueID)
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
case issueCreatedMsg:
|
case issueCreatedMsg:
|
||||||
@@ -159,9 +162,10 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
setItemsCmd := m.issueList.SetIssues(issues)
|
setItemsCmd := m.issueList.SetIssues(OpenAndInProgressOnly(issues))
|
||||||
|
closedSetCmd := m.closedIssueList.SetIssues(ClosedOnly(issues))
|
||||||
m.issueDetail.SetIssue(*msg.Issue)
|
m.issueDetail.SetIssue(*msg.Issue)
|
||||||
return m, tea.Sequence(setItemsCmd, func() tea.Msg { return selectIssueMsg{IssueID: msg.Issue.ID} })
|
return m, tea.Sequence(setItemsCmd, closedSetCmd, func() tea.Msg { return selectIssueMsg{IssueID: msg.Issue.ID} })
|
||||||
|
|
||||||
case issueDeletedMsg:
|
case issueDeletedMsg:
|
||||||
m.confirmingDelete = false
|
m.confirmingDelete = false
|
||||||
@@ -173,10 +177,11 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
setItemsCmd := m.issueList.SetIssues(issues)
|
setItemsCmd := m.issueList.SetIssues(OpenAndInProgressOnly(issues))
|
||||||
|
closedSetCmd := m.closedIssueList.SetIssues(ClosedOnly(issues))
|
||||||
if len(issues) == 0 {
|
if len(issues) == 0 {
|
||||||
m.issueDetail.SetIssue(models.Issue{})
|
m.issueDetail.SetIssue(models.Issue{})
|
||||||
return m, setItemsCmd
|
return m, tea.Sequence(setItemsCmd, closedSetCmd)
|
||||||
}
|
}
|
||||||
newIndex := msg.PreviousIndex
|
newIndex := msg.PreviousIndex
|
||||||
if newIndex >= len(issues) {
|
if newIndex >= len(issues) {
|
||||||
@@ -184,7 +189,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
selectID := issues[newIndex].ID
|
selectID := issues[newIndex].ID
|
||||||
m.issueDetail.SetIssue(issues[newIndex])
|
m.issueDetail.SetIssue(issues[newIndex])
|
||||||
return m, tea.Sequence(setItemsCmd, func() tea.Msg { return selectIssueMsg{IssueID: selectID} })
|
return m, tea.Sequence(setItemsCmd, closedSetCmd, func() tea.Msg { return selectIssueMsg{IssueID: selectID} })
|
||||||
|
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
if m.confirmingDelete {
|
if m.confirmingDelete {
|
||||||
@@ -282,8 +287,9 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.issueList.FilterState() == list.Filtering {
|
focusedList := m.FocusedIssueList()
|
||||||
cmd, _ := m.issueList.Update(msg)
|
if focusedList.FilterState() == list.Filtering {
|
||||||
|
cmd, _ := focusedList.Update(msg)
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,12 +304,20 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, changed := m.issueList.Update(msg)
|
if m.focusedWindow == 0 {
|
||||||
|
cmd, changed := m.issueList.Update(msg)
|
||||||
|
if changed {
|
||||||
|
if selected := m.issueList.SelectedItem(); selected.ID != "" {
|
||||||
|
m.issueDetail.SetIssue(selected.Issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
cmd, changed := m.closedIssueList.Update(msg)
|
||||||
if changed {
|
if changed {
|
||||||
if selected := m.issueList.SelectedItem(); selected.ID != "" {
|
if selected := m.closedIssueList.SelectedItem(); selected.ID != "" {
|
||||||
m.issueDetail.SetIssue(selected.Issue)
|
m.issueDetail.SetIssue(selected.Issue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,20 +19,36 @@ func (m *Model) View() string {
|
|||||||
bottomHeight := m.helpBar.Height()
|
bottomHeight := m.helpBar.Height()
|
||||||
|
|
||||||
contentHeight := m.height - headerHeight - bottomHeight
|
contentHeight := m.height - headerHeight - bottomHeight
|
||||||
|
halfHeight := contentHeight / 2
|
||||||
|
if halfHeight < 1 {
|
||||||
|
halfHeight = 1
|
||||||
|
}
|
||||||
|
|
||||||
totalContentWidth := m.width - 1
|
totalContentWidth := m.width - 1
|
||||||
listWidth := totalContentWidth * styles.ListViewRatio / 100
|
listWidth := totalContentWidth * styles.ListViewRatio / 100
|
||||||
detailWidth := totalContentWidth - listWidth
|
detailWidth := totalContentWidth - listWidth
|
||||||
|
|
||||||
m.issueList.SetSize(listWidth, contentHeight)
|
m.issueList.SetSize(listWidth, halfHeight)
|
||||||
|
m.closedIssueList.SetSize(listWidth, halfHeight)
|
||||||
m.issueDetail.SetSize(detailWidth, contentHeight)
|
m.issueDetail.SetSize(detailWidth, contentHeight)
|
||||||
|
|
||||||
listView := m.issueList.View()
|
listView := m.issueList.View()
|
||||||
|
closedListView := m.closedIssueList.View()
|
||||||
detailView := m.issueDetail.View()
|
detailView := m.issueDetail.View()
|
||||||
|
|
||||||
content := lipgloss.JoinHorizontal(lipgloss.Left, listView, detailView)
|
mainLabel := styles.LabelStyle.Render("Display issues")
|
||||||
|
closedLabel := styles.LabelStyle.Render("Closed issues")
|
||||||
|
if m.focusedWindow == 0 {
|
||||||
|
mainLabel = lipgloss.NewStyle().Foreground(styles.Primary).Bold(true).Render("Display issues ▶")
|
||||||
|
} else {
|
||||||
|
closedLabel = lipgloss.NewStyle().Foreground(styles.Primary).Bold(true).Render("Closed issues ▶")
|
||||||
|
}
|
||||||
|
leftColumn := lipgloss.JoinVertical(lipgloss.Left,
|
||||||
|
mainLabel, listView,
|
||||||
|
closedLabel, closedListView,
|
||||||
|
)
|
||||||
|
content := lipgloss.JoinHorizontal(lipgloss.Left, leftColumn, detailView)
|
||||||
|
|
||||||
// return lipgloss.JoinVertical(lipgloss.Left, header, content, bottomView)
|
|
||||||
mainView := lipgloss.JoinVertical(lipgloss.Left, header, content, bottomView)
|
mainView := lipgloss.JoinVertical(lipgloss.Left, header, content, bottomView)
|
||||||
|
|
||||||
if m.editingTitle {
|
if m.editingTitle {
|
||||||
|
|||||||
Reference in New Issue
Block a user