From 303c6769c8a1e67d5a454f718f0dfcbc56640206 Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Mon, 19 Aug 2024 15:12:27 +0300 Subject: [PATCH 1/8] feat: support switch view from repo + hide search bar --- ui/components/branch/branch.go | 2 +- ui/components/issuessection/issuessection.go | 1 + ui/components/prssection/prssection.go | 1 + ui/components/reposection/reposection.go | 23 ++------------------ ui/components/section/section.go | 8 ++++++- ui/ui.go | 15 +++++++++++++ 6 files changed, 27 insertions(+), 23 deletions(-) diff --git a/ui/components/branch/branch.go b/ui/components/branch/branch.go index 1b8265cf..8fbb43bc 100644 --- a/ui/components/branch/branch.go +++ b/ui/components/branch/branch.go @@ -196,7 +196,7 @@ func (b *Branch) renderExtendedTitle(isSelected bool) string { title = baseStyle.Foreground(b.Ctx.Theme.SecondaryText).Width(width).MaxWidth(width).Render(title) name := b.Data.Name if b.Data.IsCheckedOut { - name = baseStyle.Foreground(b.Ctx.Theme.SuccessText).Render(" " + name) + name = baseStyle.Foreground(b.Ctx.Theme.SuccessText).Render(name) } else { name = baseStyle.Foreground(b.Ctx.Theme.PrimaryText).Render(name) } diff --git a/ui/components/issuessection/issuessection.go b/ui/components/issuessection/issuessection.go index d46bb6cb..02cd9c1c 100644 --- a/ui/components/issuessection/issuessection.go +++ b/ui/components/issuessection/issuessection.go @@ -39,6 +39,7 @@ func NewModel( m.GetItemSingularForm(), m.GetItemPluralForm(), lastUpdated, + true, ) m.Issues = []data.IssueData{} diff --git a/ui/components/prssection/prssection.go b/ui/components/prssection/prssection.go index 9059f848..796d9b6f 100644 --- a/ui/components/prssection/prssection.go +++ b/ui/components/prssection/prssection.go @@ -42,6 +42,7 @@ func NewModel( m.GetItemSingularForm(), m.GetItemPluralForm(), lastUpdated, + true, ) m.Prs = []data.PullRequestData{} diff --git a/ui/components/reposection/reposection.go b/ui/components/reposection/reposection.go index 07fc2d69..ee42de6a 100644 --- a/ui/components/reposection/reposection.go +++ b/ui/components/reposection/reposection.go @@ -46,6 +46,7 @@ func NewModel( m.GetItemSingularForm(), m.GetItemPluralForm(), lastUpdated, + false, ) m.repo = &git.Repo{Branches: []git.Branch{}} m.Branches = []branch.Branch{} @@ -62,24 +63,6 @@ func (m Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { case tea.KeyMsg: - if m.IsSearchFocused() { - switch { - - case msg.Type == tea.KeyCtrlC, msg.Type == tea.KeyEsc: - m.SearchBar.SetValue(m.SearchValue) - blinkCmd := m.SetIsSearching(false) - return &m, blinkCmd - - case msg.Type == tea.KeyEnter: - m.SearchValue = m.SearchBar.Value() - m.SetIsSearching(false) - m.ResetRows() - return &m, tea.Batch(m.FetchNextPageSectionRows()...) - } - - break - } - if m.IsPromptConfirmationFocused() { switch { case msg.Type == tea.KeyCtrlC, msg.Type == tea.KeyEsc: @@ -171,9 +154,7 @@ func (m Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { } } - search, searchCmd := m.SearchBar.Update(msg) m.Table.SetRows(m.BuildRows()) - m.SearchBar = search prompt, promptCmd := m.PromptConfirmationBox.Update(msg) m.PromptConfirmationBox = prompt @@ -181,7 +162,7 @@ func (m Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { table, tableCmd := m.Table.Update(msg) m.Table = table - return &m, tea.Batch(cmd, searchCmd, promptCmd, tableCmd) + return &m, tea.Batch(cmd, promptCmd, tableCmd) } func GetSectionColumns( diff --git a/ui/components/section/section.go b/ui/components/section/section.go index 8d8be9ed..5c5f91ba 100644 --- a/ui/components/section/section.go +++ b/ui/components/section/section.go @@ -38,6 +38,7 @@ type BaseModel struct { IsPromptConfirmationShown bool PromptConfirmationAction string LastFetchTaskId string + IsSearchSupported bool } func NewModel( @@ -48,6 +49,7 @@ func NewModel( columns []table.Column, singular, plural string, lastUpdated time.Time, + isSearchSupported bool, ) BaseModel { m := BaseModel{ Id: id, @@ -64,6 +66,7 @@ func NewModel( TotalCount: 0, PageInfo: nil, PromptConfirmationBox: prompt.NewModel(ctx), + IsSearchSupported: isSearchSupported, } m.Table = table.NewModel( *ctx, @@ -308,7 +311,10 @@ func (m *BaseModel) GetMainContent() string { } func (m *BaseModel) View() string { - search := m.SearchBar.View(*m.Ctx) + search := "" + if m.IsSearchSupported { + search = m.SearchBar.View(*m.Ctx) + } return m.Ctx.Styles.Section.ContainerStyle.Render( lipgloss.JoinVertical( diff --git a/ui/ui.go b/ui/ui.go index 7a54375c..81939666 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -284,6 +284,21 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmd = currSection.SetIsPromptConfirmationShown(true) } return m, cmd + + case key.Matches(msg, keys.BranchKeys.ViewPRs): + m.ctx.View = m.switchSelectedView() + m.syncMainContentWidth() + m.setCurrSectionId(1) + m.tabs.UpdateSectionsConfigs(&m.ctx) + + currSections := m.getCurrentViewSections() + if len(currSections) == 0 { + newSections, fetchSectionsCmds := m.fetchAllViewSections() + m.setCurrentViewSections(newSections) + cmd = fetchSectionsCmds + } + m.onViewedRowChanged() + } case m.ctx.View == config.PRsView, m.ctx.View == config.RepoView: switch { From 24af3108a2282463bbd9afef9e80469054c95b34 Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Mon, 19 Aug 2024 17:30:12 +0300 Subject: [PATCH 2/8] refactor: section options WIP WIP --- ui/components/issuessection/issuessection.go | 18 +- ui/components/listviewport/listviewport.go | 3 + ui/components/prssection/prssection.go | 18 +- ui/components/reposection/checkout.go | 10 +- ui/components/reposection/delete.go | 10 +- ui/components/reposection/reposection.go | 269 ++++++++++--------- ui/components/section/section.go | 39 +-- ui/components/table/table.go | 2 + ui/context/styles.go | 1 - ui/ui.go | 54 ++-- 10 files changed, 227 insertions(+), 197 deletions(-) diff --git a/ui/components/issuessection/issuessection.go b/ui/components/issuessection/issuessection.go index 02cd9c1c..fe8f5635 100644 --- a/ui/components/issuessection/issuessection.go +++ b/ui/components/issuessection/issuessection.go @@ -31,15 +31,17 @@ func NewModel( ) Model { m := Model{} m.BaseModel = section.NewModel( - id, ctx, - cfg.ToSectionConfig(), - SectionType, - GetSectionColumns(cfg, ctx), - m.GetItemSingularForm(), - m.GetItemPluralForm(), - lastUpdated, - true, + section.NewSectionOptions{ + Id: id, + Config: cfg.ToSectionConfig(), + Type: SectionType, + Columns: GetSectionColumns(cfg, ctx), + Singular: m.GetItemSingularForm(), + Plural: m.GetItemPluralForm(), + LastUpdated: lastUpdated, + IsSearchSupported: true, + }, ) m.Issues = []data.IssueData{} diff --git a/ui/components/listviewport/listviewport.go b/ui/components/listviewport/listviewport.go index 66abf296..8591a3f7 100644 --- a/ui/components/listviewport/listviewport.go +++ b/ui/components/listviewport/listviewport.go @@ -65,6 +65,9 @@ func (m *Model) SyncViewPort(content string) { } func (m *Model) getNumPrsPerPage() int { + if m.ListItemHeight == 0 { + return 0 + } return m.viewport.Height / m.ListItemHeight } diff --git a/ui/components/prssection/prssection.go b/ui/components/prssection/prssection.go index 796d9b6f..e0330ef6 100644 --- a/ui/components/prssection/prssection.go +++ b/ui/components/prssection/prssection.go @@ -34,15 +34,17 @@ func NewModel( ) Model { m := Model{} m.BaseModel = section.NewModel( - id, ctx, - cfg.ToSectionConfig(), - SectionType, - GetSectionColumns(cfg, ctx), - m.GetItemSingularForm(), - m.GetItemPluralForm(), - lastUpdated, - true, + section.NewSectionOptions{ + Id: id, + Config: cfg.ToSectionConfig(), + Type: SectionType, + Columns: GetSectionColumns(cfg, ctx), + Singular: m.GetItemSingularForm(), + Plural: m.GetItemPluralForm(), + LastUpdated: lastUpdated, + IsSearchSupported: true, + }, ) m.Prs = []data.PullRequestData{} diff --git a/ui/components/reposection/checkout.go b/ui/components/reposection/checkout.go index be09616c..69d23988 100644 --- a/ui/components/reposection/checkout.go +++ b/ui/components/reposection/checkout.go @@ -23,20 +23,20 @@ func (m *Model) checkout() (tea.Cmd, error) { State: context.TaskStart, Error: nil, } - startCmd := m.Ctx.StartTask(task) + startCmd := m.ctx.StartTask(task) return tea.Batch(startCmd, func() tea.Msg { - err := gitm.Checkout(*m.Ctx.RepoPath, b.Data.Name) + err := gitm.Checkout(*m.ctx.RepoPath, b.Data.Name) if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} } - repo, err := git.GetRepo(*m.Ctx.RepoPath) + repo, err := git.GetRepo(*m.ctx.RepoPath) if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} } return constants.TaskFinishedMsg{ - SectionId: m.Id, - SectionType: m.Type, + SectionId: 0, + SectionType: SectionType, TaskId: taskId, Msg: repoMsg{repo: repo}, Err: err, diff --git a/ui/components/reposection/delete.go b/ui/components/reposection/delete.go index 1db20ad1..8b0ad64a 100644 --- a/ui/components/reposection/delete.go +++ b/ui/components/reposection/delete.go @@ -23,20 +23,20 @@ func (m *Model) delete() (tea.Cmd, error) { State: context.TaskStart, Error: nil, } - startCmd := m.Ctx.StartTask(task) + startCmd := m.ctx.StartTask(task) return tea.Batch(startCmd, func() tea.Msg { - err := gitm.DeleteBranch(*m.Ctx.RepoPath, b.Data.Name, gitm.DeleteBranchOptions{Force: true}) + err := gitm.DeleteBranch(*m.ctx.RepoPath, b.Data.Name, gitm.DeleteBranchOptions{Force: true}) if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} } - repo, err := git.GetRepo(*m.Ctx.RepoPath) + repo, err := git.GetRepo(*m.ctx.RepoPath) if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} } return constants.TaskFinishedMsg{ - SectionId: m.Id, - SectionType: m.Type, + SectionId: 0, + SectionType: SectionType, TaskId: taskId, Msg: repoMsg{repo: repo}, Err: err, diff --git a/ui/components/reposection/reposection.go b/ui/components/reposection/reposection.go index ee42de6a..d91e5639 100644 --- a/ui/components/reposection/reposection.go +++ b/ui/components/reposection/reposection.go @@ -8,12 +8,12 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" "github.com/dlvhdr/gh-dash/v4/config" "github.com/dlvhdr/gh-dash/v4/data" "github.com/dlvhdr/gh-dash/v4/git" "github.com/dlvhdr/gh-dash/v4/ui/components/branch" - "github.com/dlvhdr/gh-dash/v4/ui/components/section" "github.com/dlvhdr/gh-dash/v4/ui/components/table" "github.com/dlvhdr/gh-dash/v4/ui/constants" "github.com/dlvhdr/gh-dash/v4/ui/context" @@ -24,7 +24,9 @@ import ( const SectionType = "repo" type Model struct { - section.BaseModel + ctx *context.ProgramContext + cfg config.PrsSectionConfig + table table.Model repo *git.Repo Branches []branch.Branch Prs []data.PullRequestData @@ -37,16 +39,18 @@ func NewModel( lastUpdated time.Time, ) Model { m := Model{} - m.BaseModel = section.NewModel( - id, - ctx, - cfg.ToSectionConfig(), - SectionType, - GetSectionColumns(cfg, ctx), - m.GetItemSingularForm(), - m.GetItemPluralForm(), + m.ctx = ctx + m.cfg = cfg + m.table = table.NewModel( + *ctx, + m.GetDimensions(), lastUpdated, - false, + GetSectionColumns(ctx, cfg), + nil, + "Branch", + utils.StringPtr("No branches"), + "Loading branches...", + true, ) m.repo = &git.Repo{Branches: []git.Branch{}} m.Branches = []branch.Branch{} @@ -55,7 +59,7 @@ func NewModel( return m } -func (m Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { var cmd tea.Cmd var err error @@ -63,40 +67,40 @@ func (m Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { case tea.KeyMsg: - if m.IsPromptConfirmationFocused() { - switch { - case msg.Type == tea.KeyCtrlC, msg.Type == tea.KeyEsc: - m.PromptConfirmationBox.Reset() - cmd = m.SetIsPromptConfirmationShown(false) - return &m, cmd - - case msg.Type == tea.KeyEnter: - input := m.PromptConfirmationBox.Value() - action := m.GetPromptConfirmationAction() - if input == "Y" || input == "y" { - switch action { - case "delete": - cmd, err = m.delete() - if err != nil { - m.Ctx.Error = err - } - } - } - - m.PromptConfirmationBox.Reset() - blinkCmd := m.SetIsPromptConfirmationShown(false) - - return &m, tea.Batch(cmd, blinkCmd) - } - - break - } + // if m.IsPromptConfirmationFocused() { + // switch { + // case msg.Type == tea.KeyCtrlC, msg.Type == tea.KeyEsc: + // m.PromptConfirmationBox.Reset() + // cmd = m.SetIsPromptConfirmationShown(false) + // return &m, cmd + // + // case msg.Type == tea.KeyEnter: + // input := m.PromptConfirmationBox.Value() + // action := m.GetPromptConfirmationAction() + // if input == "Y" || input == "y" { + // switch action { + // case "delete": + // cmd, err = m.delete() + // if err != nil { + // m.Ctx.Error = err + // } + // } + // } + // + // m.PromptConfirmationBox.Reset() + // blinkCmd := m.SetIsPromptConfirmationShown(false) + // + // return &m, tea.Batch(cmd, blinkCmd) + // } + // + // break + // } switch { case key.Matches(msg, keys.BranchKeys.Checkout): cmd, err = m.checkout() if err != nil { - m.Ctx.Error = err + m.ctx.Error = err } } @@ -128,46 +132,65 @@ func (m Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { currPr.Mergeable = "" } m.Prs[i] = currPr - m.Table.SetIsLoading(false) - m.Table.SetRows(m.BuildRows()) + m.table.SetIsLoading(false) + m.table.SetRows(m.BuildRows()) break } } case repoMsg: m.repo = msg.repo - m.Table.SetIsLoading(false) + m.table.SetIsLoading(false) m.updateBranches() - m.Table.SetRows(m.BuildRows()) - m.Table.ResetCurrItem() + m.table.SetRows(m.BuildRows()) + m.table.ResetCurrItem() case SectionPullRequestsFetchedMsg: - if m.LastFetchTaskId == msg.TaskId { - m.Prs = msg.Prs - m.TotalCount = msg.TotalCount - m.PageInfo = &msg.PageInfo - m.Table.SetIsLoading(false) - m.updateBranches() - m.Table.SetRows(m.BuildRows()) - m.Table.UpdateLastUpdated(time.Now()) - m.UpdateTotalItemsCount(m.TotalCount) - } + // if m.LastFetchTaskId == msg.TaskId { + // m.Prs = msg.Prs + // m.TotalCount = msg.TotalCount + // m.PageInfo = &msg.PageInfo + // m.table.SetIsLoading(false) + // m.updateBranches() + // m.table.SetRows(m.BuildRows()) + // m.table.UpdateLastUpdated(time.Now()) + // m.UpdateTotalItemsCount(m.TotalCount) + // } } - m.Table.SetRows(m.BuildRows()) + m.table.SetRows(m.BuildRows()) - prompt, promptCmd := m.PromptConfirmationBox.Update(msg) - m.PromptConfirmationBox = prompt + // prompt, promptCmd := m.PromptConfirmationBox.Update(msg) + // m.PromptConfirmationBox = prompt - table, tableCmd := m.Table.Update(msg) - m.Table = table + table, tableCmd := m.table.Update(msg) + m.table = table + + return m, tea.Batch(cmd, tableCmd) +} - return &m, tea.Batch(cmd, promptCmd, tableCmd) +func (m Model) View() string { + view := "" + if m.table.Rows == nil { + d := m.GetDimensions() + view = lipgloss.Place( + d.Width, + d.Height, + lipgloss.Center, + lipgloss.Center, + "No local branches", + ) + } else { + view = m.table.View() + } + return m.ctx.Styles.Section.ContainerStyle.Render( + view, + ) } func GetSectionColumns( - cfg config.PrsSectionConfig, ctx *context.ProgramContext, + cfg config.PrsSectionConfig, ) []table.Column { dLayout := ctx.Config.Defaults.Layout.Prs sLayout := cfg.Layout @@ -296,7 +319,7 @@ func GetSectionColumns( func (m *Model) updateBranches() { branches := make([]branch.Branch, 0) for _, ref := range m.repo.Branches { - b := branch.Branch{Ctx: m.Ctx, Data: ref, Columns: m.Table.Columns} + b := branch.Branch{Ctx: m.ctx, Data: ref, Columns: m.table.Columns} b.PR = findPRForRef(m.Prs, ref.Name) branches = append(branches, b) @@ -322,7 +345,7 @@ func (m *Model) updateBranches() { func (m Model) BuildRows() []table.Row { var rows []table.Row - currItem := m.Table.GetCurrItem() + currItem := m.table.GetCurrItem() sorted := m.Branches @@ -364,14 +387,14 @@ func (m *Model) GetCurrBranch() *branch.Branch { if len(m.repo.Branches) == 0 { return nil } - return &m.Branches[m.Table.GetCurrItem()] + return &m.Branches[m.table.GetCurrItem()] } func (m *Model) GetCurrRow() data.RowData { if len(m.repo.Branches) == 0 { return nil } - branch := m.repo.Branches[m.Table.GetCurrItem()] + branch := m.repo.Branches[m.table.GetCurrItem()] pr := findPRForRef(m.Prs, branch.Name) return pr } @@ -381,91 +404,77 @@ func (m *Model) FetchNextPageSectionRows() []tea.Cmd { return nil } - if m.PageInfo != nil && !m.PageInfo.HasNextPage { - return nil - } - var cmds []tea.Cmd - startCursor := time.Now().String() - if m.PageInfo != nil { - startCursor = m.PageInfo.StartCursor - } - taskId := fmt.Sprintf("fetching_prs_%d_%s", m.Id, startCursor) - m.LastFetchTaskId = taskId - branchesTaskId := fmt.Sprintf("fetching_branches_%d", time.Now().Unix()) - if m.Ctx.RepoPath != nil { + if m.ctx.RepoPath != nil { branchesTask := context.Task{ Id: branchesTaskId, StartText: "Reading local branches", FinishedText: fmt.Sprintf( `Read branches successfully for "%s"`, - *m.Ctx.RepoPath, + *m.ctx.RepoPath, ), State: context.TaskStart, Error: nil, } - bCmd := m.Ctx.StartTask(branchesTask) + bCmd := m.ctx.StartTask(branchesTask) cmds = append(cmds, bCmd) } + prsTaskId := fmt.Sprintf("fetching_pr_branches", time.Now().Unix()) task := context.Task{ - Id: taskId, + Id: prsTaskId, StartText: "Fetching PRs for your branches", FinishedText: "PRs for your branches have been fetched", State: context.TaskStart, Error: nil, } - startCmd := m.Ctx.StartTask(task) + startCmd := m.ctx.StartTask(task) cmds = append(cmds, startCmd) var repoCmd tea.Cmd - if m.Ctx.RepoPath != nil { + if m.ctx.RepoPath != nil { repoCmd = m.makeRepoCmd(branchesTaskId) } fetchCmd := func() tea.Msg { - limit := m.Config.Limit + limit := m.cfg.Limit if limit == nil { - limit = &m.Ctx.Config.Defaults.PrsLimit + limit = &m.ctx.Config.Defaults.PrsLimit } - res, err := data.FetchPullRequests(m.GetFilters(), *limit, m.PageInfo) + res, err := data.FetchPullRequests("author:@me", *limit, nil) // TODO: enrich with branches only for section with branches if err != nil { return constants.TaskFinishedMsg{ - SectionId: m.Id, - SectionType: m.Type, - TaskId: taskId, + SectionId: 0, + SectionType: SectionType, + TaskId: prsTaskId, Err: err, } } return constants.TaskFinishedMsg{ - SectionId: m.Id, - SectionType: m.Type, - TaskId: taskId, + SectionId: 0, + SectionType: SectionType, + TaskId: prsTaskId, Msg: SectionPullRequestsFetchedMsg{ Prs: res.Prs, TotalCount: res.TotalCount, PageInfo: res.PageInfo, - TaskId: taskId, + TaskId: prsTaskId, }, } } cmds = append(cmds, fetchCmd, repoCmd) - if m.PageInfo == nil { - m.Table.SetIsLoading(true) - cmds = append(cmds, m.Table.StartLoadingSpinner()) - - } + m.table.SetIsLoading(true) + cmds = append(cmds, m.table.StartLoadingSpinner()) return cmds } func (m *Model) ResetRows() { m.Prs = nil - m.BaseModel.ResetRows() } type repoMsg struct { @@ -480,9 +489,7 @@ func openRepoCmd(dir string) tea.Cmd { } } -func FetchAllBranches( - ctx context.ProgramContext, -) (sections []section.Section, fetchAllCmd tea.Cmd) { +func FetchAllBranches(ctx context.ProgramContext) (Model, tea.Cmd) { cmds := make([]tea.Cmd, 0) if ctx.RepoPath != nil { @@ -496,15 +503,15 @@ func FetchAllBranches( Limit: utils.IntPtr(20), Type: &t, } - s := NewModel( + m := NewModel( 1, &ctx, cfg, time.Now(), ) - cmds = append(cmds, s.FetchNextPageSectionRows()...) + cmds = append(cmds, m.FetchNextPageSectionRows()...) - return []section.Section{&s}, tea.Batch(cmds...) + return m, tea.Batch(cmds...) } type UpdatePRMsg struct { @@ -550,34 +557,46 @@ func assigneesContains(assignees []data.Assignee, assignee data.Assignee) bool { return false } -func (m Model) GetItemSingularForm() string { - return "PR" -} - -func (m Model) GetItemPluralForm() string { - return "PRs" -} - -func (m Model) GetTotalCount() *int { - if m.IsLoading() { - return nil - } - return &m.TotalCount -} - func (m Model) IsLoading() bool { - return m.Table.IsLoading() + return m.table.IsLoading() } func (m *Model) makeRepoCmd(taskId string) tea.Cmd { return func() tea.Msg { - repo, err := git.GetRepo(*m.Ctx.RepoPath) + repo, err := git.GetRepo(*m.ctx.RepoPath) return constants.TaskFinishedMsg{ - SectionId: m.Id, - SectionType: m.Type, + SectionId: 0, + SectionType: SectionType, TaskId: taskId, Msg: repoMsg{repo: repo}, Err: err, } } } + +func (m Model) GetDimensions() constants.Dimensions { + if m.ctx == nil { + return constants.Dimensions{} + } + return constants.Dimensions{ + Width: m.ctx.MainContentWidth - m.ctx.Styles.Section.ContainerStyle.GetHorizontalPadding(), + Height: m.ctx.MainContentHeight, + } +} + +func (m *Model) UpdateProgramContext(ctx *context.ProgramContext) { + oldDimensions := m.GetDimensions() + m.ctx = ctx + newDimensions := m.GetDimensions() + tableDimensions := constants.Dimensions{ + Height: newDimensions.Height, + Width: newDimensions.Width, + } + m.table.SetDimensions(tableDimensions) + m.table.UpdateProgramContext(ctx) + + if oldDimensions.Height != newDimensions.Height || + oldDimensions.Width != newDimensions.Width { + m.table.SyncViewPortContent() + } +} diff --git a/ui/components/section/section.go b/ui/components/section/section.go index 5c5f91ba..163da268 100644 --- a/ui/components/section/section.go +++ b/ui/components/section/section.go @@ -41,37 +41,42 @@ type BaseModel struct { IsSearchSupported bool } +type NewSectionOptions struct { + Id int + Config config.SectionConfig + IsSearchSupported bool + Type string + Columns []table.Column + Singular string + Plural string + LastUpdated time.Time +} + func NewModel( - id int, ctx *context.ProgramContext, - cfg config.SectionConfig, - sType string, - columns []table.Column, - singular, plural string, - lastUpdated time.Time, - isSearchSupported bool, + options NewSectionOptions, ) BaseModel { m := BaseModel{ - Id: id, - Type: sType, - Config: cfg, Ctx: ctx, + Id: options.Id, + Type: options.Type, + Config: options.Config, Spinner: spinner.Model{Spinner: spinner.Dot}, - Columns: columns, - SingularForm: singular, - PluralForm: plural, - SearchBar: search.NewModel(sType, ctx, cfg.Filters), - SearchValue: cfg.Filters, + Columns: options.Columns, + SingularForm: options.Singular, + PluralForm: options.Plural, + SearchBar: search.NewModel(options.Type, ctx, options.Config.Filters), + SearchValue: options.Config.Filters, IsSearching: false, TotalCount: 0, PageInfo: nil, PromptConfirmationBox: prompt.NewModel(ctx), - IsSearchSupported: isSearchSupported, + IsSearchSupported: options.IsSearchSupported, } m.Table = table.NewModel( *ctx, m.GetDimensions(), - lastUpdated, + options.LastUpdated, m.Columns, nil, m.SingularForm, diff --git a/ui/components/table/table.go b/ui/components/table/table.go index a527717b..4dd73155 100644 --- a/ui/components/table/table.go +++ b/ui/components/table/table.go @@ -7,6 +7,7 @@ import ( "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/log" "github.com/dlvhdr/gh-dash/v4/ui/common" "github.com/dlvhdr/gh-dash/v4/ui/components/listviewport" @@ -54,6 +55,7 @@ func NewModel( if ctx.Config.Theme.Ui.Table.ShowSeparator { itemHeight += 1 } + log.Debug("ctx", "itemHeight", itemHeight) loadingSpinner := spinner.New() loadingSpinner.Spinner = spinner.Dot diff --git a/ui/context/styles.go b/ui/context/styles.go index 471c0bb5..0ad2c7c3 100644 --- a/ui/context/styles.go +++ b/ui/context/styles.go @@ -197,7 +197,6 @@ func InitStyles(theme theme.Theme) Styles { Faint(true). Padding(0, 2) s.Tabs.ActiveTab = s.Tabs.Tab. - Copy(). Faint(false). Bold(true). Background(theme.SelectedBackground). diff --git a/ui/ui.go b/ui/ui.go index 81939666..aec92638 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -41,7 +41,7 @@ type Model struct { issueSidebar issuesidebar.Model currSectionId int footer footer.Model - repo []section.Section + repo reposection.Model prs []section.Section issues []section.Section tabs tabs.Model @@ -554,20 +554,24 @@ func (m Model) View() string { } s := strings.Builder{} - s.WriteString(m.tabs.View(m.ctx)) + if m.ctx.View != config.RepoView { + s.WriteString(m.tabs.View(m.ctx)) + } s.WriteString("\n") - currSection := m.getCurrSection() - mainContent := "" - if currSection != nil { - mainContent = lipgloss.JoinHorizontal( - lipgloss.Top, - m.getCurrSection().View(), - m.sidebar.View(), - ) + content := "No sections defined" + if m.ctx.View == config.RepoView { + content = m.repo.View() } else { - mainContent = "No sections defined..." + currSection := m.getCurrSection() + if currSection != nil { + content = lipgloss.JoinHorizontal( + lipgloss.Top, + m.getCurrSection().View(), + m.sidebar.View(), + ) + } } - s.WriteString(mainContent) + s.WriteString(content) s.WriteString("\n") if m.ctx.Error != nil { s.WriteString( @@ -617,6 +621,7 @@ func (m *Model) syncProgramContext() { for _, section := range m.getCurrentViewSections() { section.UpdateProgramContext(&m.ctx) } + m.repo.UpdateProgramContext(&m.ctx) m.footer.UpdateProgramContext(&m.ctx) m.sidebar.UpdateProgramContext(&m.ctx) m.prSidebar.UpdateProgramContext(&m.ctx) @@ -627,8 +632,7 @@ func (m *Model) updateSection(id int, sType string, msg tea.Msg) (cmd tea.Cmd) { var updatedSection section.Section switch sType { case reposection.SectionType: - updatedSection, cmd = m.repo[id].Update(msg) - m.repo[id] = updatedSection + m.repo, cmd = m.repo.Update(msg) case prssection.SectionType: updatedSection, cmd = m.prs[id].Update(msg) m.prs[id] = updatedSection @@ -688,7 +692,9 @@ func (m *Model) syncSidebar() { func (m *Model) fetchAllViewSections() ([]section.Section, tea.Cmd) { if m.ctx.View == config.RepoView { - return reposection.FetchAllBranches(m.ctx) + var cmd tea.Cmd + m.repo, cmd = reposection.FetchAllBranches(m.ctx) + return nil, cmd } else if m.ctx.View == config.PRsView { return prssection.FetchAllSections(m.ctx) } else { @@ -698,7 +704,7 @@ func (m *Model) fetchAllViewSections() ([]section.Section, tea.Cmd) { func (m *Model) getCurrentViewSections() []section.Section { if m.ctx.View == config.RepoView { - return m.repo + return []section.Section{} } else if m.ctx.View == config.PRsView { return m.prs } else { @@ -707,18 +713,10 @@ func (m *Model) getCurrentViewSections() []section.Section { } func (m *Model) setCurrentViewSections(newSections []section.Section) { - if m.ctx.View == config.RepoView { - search := prssection.NewModel( - 0, - &m.ctx, - config.PrsSectionConfig{ - Title: "", - Filters: "archived:false", - }, - time.Now(), - ) - m.repo = append([]section.Section{&search}, newSections...) - } else if m.ctx.View == config.PRsView { + if newSections == nil { + return + } + if m.ctx.View == config.PRsView { search := prssection.NewModel( 0, &m.ctx, From 8f16d2e9b18afa7b4f2ea15f0cca3600b3ef27ef Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Fri, 23 Aug 2024 16:32:09 +0300 Subject: [PATCH 3/8] chore: make reposection an actual section --- ui/components/reposection/checkout.go | 6 +- ui/components/reposection/delete.go | 6 +- ui/components/reposection/reposection.go | 126 +++++++++++++---------- ui/ui.go | 26 +++-- 4 files changed, 94 insertions(+), 70 deletions(-) diff --git a/ui/components/reposection/checkout.go b/ui/components/reposection/checkout.go index 69d23988..28ead9a1 100644 --- a/ui/components/reposection/checkout.go +++ b/ui/components/reposection/checkout.go @@ -23,13 +23,13 @@ func (m *Model) checkout() (tea.Cmd, error) { State: context.TaskStart, Error: nil, } - startCmd := m.ctx.StartTask(task) + startCmd := m.Ctx.StartTask(task) return tea.Batch(startCmd, func() tea.Msg { - err := gitm.Checkout(*m.ctx.RepoPath, b.Data.Name) + err := gitm.Checkout(*m.Ctx.RepoPath, b.Data.Name) if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} } - repo, err := git.GetRepo(*m.ctx.RepoPath) + repo, err := git.GetRepo(*m.Ctx.RepoPath) if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} } diff --git a/ui/components/reposection/delete.go b/ui/components/reposection/delete.go index 8b0ad64a..426d2ef8 100644 --- a/ui/components/reposection/delete.go +++ b/ui/components/reposection/delete.go @@ -23,13 +23,13 @@ func (m *Model) delete() (tea.Cmd, error) { State: context.TaskStart, Error: nil, } - startCmd := m.ctx.StartTask(task) + startCmd := m.Ctx.StartTask(task) return tea.Batch(startCmd, func() tea.Msg { - err := gitm.DeleteBranch(*m.ctx.RepoPath, b.Data.Name, gitm.DeleteBranchOptions{Force: true}) + err := gitm.DeleteBranch(*m.Ctx.RepoPath, b.Data.Name, gitm.DeleteBranchOptions{Force: true}) if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} } - repo, err := git.GetRepo(*m.ctx.RepoPath) + repo, err := git.GetRepo(*m.Ctx.RepoPath) if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} } diff --git a/ui/components/reposection/reposection.go b/ui/components/reposection/reposection.go index d91e5639..f44ca524 100644 --- a/ui/components/reposection/reposection.go +++ b/ui/components/reposection/reposection.go @@ -14,6 +14,7 @@ import ( "github.com/dlvhdr/gh-dash/v4/data" "github.com/dlvhdr/gh-dash/v4/git" "github.com/dlvhdr/gh-dash/v4/ui/components/branch" + "github.com/dlvhdr/gh-dash/v4/ui/components/section" "github.com/dlvhdr/gh-dash/v4/ui/components/table" "github.com/dlvhdr/gh-dash/v4/ui/constants" "github.com/dlvhdr/gh-dash/v4/ui/context" @@ -24,9 +25,7 @@ import ( const SectionType = "repo" type Model struct { - ctx *context.ProgramContext - cfg config.PrsSectionConfig - table table.Model + section.BaseModel repo *git.Repo Branches []branch.Branch Prs []data.PullRequestData @@ -39,18 +38,18 @@ func NewModel( lastUpdated time.Time, ) Model { m := Model{} - m.ctx = ctx - m.cfg = cfg - m.table = table.NewModel( - *ctx, - m.GetDimensions(), - lastUpdated, - GetSectionColumns(ctx, cfg), - nil, - "Branch", - utils.StringPtr("No branches"), - "Loading branches...", - true, + m.BaseModel = section.NewModel( + ctx, + section.NewSectionOptions{ + Id: id, + Config: cfg.ToSectionConfig(), + Type: SectionType, + Columns: GetSectionColumns(ctx, cfg), + Singular: "branch", + Plural: "branches", + LastUpdated: lastUpdated, + IsSearchSupported: false, + }, ) m.repo = &git.Repo{Branches: []git.Branch{}} m.Branches = []branch.Branch{} @@ -59,7 +58,7 @@ func NewModel( return m } -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { +func (m *Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { var cmd tea.Cmd var err error @@ -100,7 +99,7 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { case key.Matches(msg, keys.BranchKeys.Checkout): cmd, err = m.checkout() if err != nil { - m.ctx.Error = err + m.Ctx.Error = err } } @@ -132,46 +131,46 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { currPr.Mergeable = "" } m.Prs[i] = currPr - m.table.SetIsLoading(false) - m.table.SetRows(m.BuildRows()) + m.Table.SetIsLoading(false) + m.Table.SetRows(m.BuildRows()) break } } case repoMsg: m.repo = msg.repo - m.table.SetIsLoading(false) + m.Table.SetIsLoading(false) m.updateBranches() - m.table.SetRows(m.BuildRows()) - m.table.ResetCurrItem() + m.Table.SetRows(m.BuildRows()) + m.Table.ResetCurrItem() case SectionPullRequestsFetchedMsg: // if m.LastFetchTaskId == msg.TaskId { // m.Prs = msg.Prs // m.TotalCount = msg.TotalCount // m.PageInfo = &msg.PageInfo - // m.table.SetIsLoading(false) + // m.Table.SetIsLoading(false) // m.updateBranches() - // m.table.SetRows(m.BuildRows()) - // m.table.UpdateLastUpdated(time.Now()) + // m.Table.SetRows(m.BuildRows()) + // m.Table.UpdateLastUpdated(time.Now()) // m.UpdateTotalItemsCount(m.TotalCount) // } } - m.table.SetRows(m.BuildRows()) + m.Table.SetRows(m.BuildRows()) // prompt, promptCmd := m.PromptConfirmationBox.Update(msg) // m.PromptConfirmationBox = prompt - table, tableCmd := m.table.Update(msg) - m.table = table + table, tableCmd := m.Table.Update(msg) + m.Table = table return m, tea.Batch(cmd, tableCmd) } -func (m Model) View() string { +func (m *Model) View() string { view := "" - if m.table.Rows == nil { + if m.Table.Rows == nil { d := m.GetDimensions() view = lipgloss.Place( d.Width, @@ -181,9 +180,9 @@ func (m Model) View() string { "No local branches", ) } else { - view = m.table.View() + view = m.Table.View() } - return m.ctx.Styles.Section.ContainerStyle.Render( + return m.Ctx.Styles.Section.ContainerStyle.Render( view, ) } @@ -319,7 +318,7 @@ func GetSectionColumns( func (m *Model) updateBranches() { branches := make([]branch.Branch, 0) for _, ref := range m.repo.Branches { - b := branch.Branch{Ctx: m.ctx, Data: ref, Columns: m.table.Columns} + b := branch.Branch{Ctx: m.Ctx, Data: ref, Columns: m.Table.Columns} b.PR = findPRForRef(m.Prs, ref.Name) branches = append(branches, b) @@ -345,7 +344,7 @@ func (m *Model) updateBranches() { func (m Model) BuildRows() []table.Row { var rows []table.Row - currItem := m.table.GetCurrItem() + currItem := m.Table.GetCurrItem() sorted := m.Branches @@ -387,14 +386,14 @@ func (m *Model) GetCurrBranch() *branch.Branch { if len(m.repo.Branches) == 0 { return nil } - return &m.Branches[m.table.GetCurrItem()] + return &m.Branches[m.Table.GetCurrItem()] } func (m *Model) GetCurrRow() data.RowData { if len(m.repo.Branches) == 0 { return nil } - branch := m.repo.Branches[m.table.GetCurrItem()] + branch := m.repo.Branches[m.Table.GetCurrItem()] pr := findPRForRef(m.Prs, branch.Name) return pr } @@ -407,18 +406,18 @@ func (m *Model) FetchNextPageSectionRows() []tea.Cmd { var cmds []tea.Cmd branchesTaskId := fmt.Sprintf("fetching_branches_%d", time.Now().Unix()) - if m.ctx.RepoPath != nil { + if m.Ctx.RepoPath != nil { branchesTask := context.Task{ Id: branchesTaskId, StartText: "Reading local branches", FinishedText: fmt.Sprintf( `Read branches successfully for "%s"`, - *m.ctx.RepoPath, + *m.Ctx.RepoPath, ), State: context.TaskStart, Error: nil, } - bCmd := m.ctx.StartTask(branchesTask) + bCmd := m.Ctx.StartTask(branchesTask) cmds = append(cmds, bCmd) } @@ -430,17 +429,17 @@ func (m *Model) FetchNextPageSectionRows() []tea.Cmd { State: context.TaskStart, Error: nil, } - startCmd := m.ctx.StartTask(task) + startCmd := m.Ctx.StartTask(task) cmds = append(cmds, startCmd) var repoCmd tea.Cmd - if m.ctx.RepoPath != nil { + if m.Ctx.RepoPath != nil { repoCmd = m.makeRepoCmd(branchesTaskId) } fetchCmd := func() tea.Msg { - limit := m.cfg.Limit + limit := m.Config.Limit if limit == nil { - limit = &m.ctx.Config.Defaults.PrsLimit + limit = &m.Ctx.Config.Defaults.PrsLimit } res, err := data.FetchPullRequests("author:@me", *limit, nil) // TODO: enrich with branches only for section with branches @@ -467,8 +466,8 @@ func (m *Model) FetchNextPageSectionRows() []tea.Cmd { } cmds = append(cmds, fetchCmd, repoCmd) - m.table.SetIsLoading(true) - cmds = append(cmds, m.table.StartLoadingSpinner()) + m.Table.SetIsLoading(true) + cmds = append(cmds, m.Table.StartLoadingSpinner()) return cmds } @@ -490,7 +489,6 @@ func openRepoCmd(dir string) tea.Cmd { } func FetchAllBranches(ctx context.ProgramContext) (Model, tea.Cmd) { - cmds := make([]tea.Cmd, 0) if ctx.RepoPath != nil { cmds = append(cmds, openRepoCmd(*ctx.RepoPath)) @@ -557,13 +555,13 @@ func assigneesContains(assignees []data.Assignee, assignee data.Assignee) bool { return false } -func (m Model) IsLoading() bool { - return m.table.IsLoading() +func (m *Model) IsLoading() bool { + return m.Table.IsLoading() } func (m *Model) makeRepoCmd(taskId string) tea.Cmd { return func() tea.Msg { - repo, err := git.GetRepo(*m.ctx.RepoPath) + repo, err := git.GetRepo(*m.Ctx.RepoPath) return constants.TaskFinishedMsg{ SectionId: 0, SectionType: SectionType, @@ -575,28 +573,44 @@ func (m *Model) makeRepoCmd(taskId string) tea.Cmd { } func (m Model) GetDimensions() constants.Dimensions { - if m.ctx == nil { + if m.Ctx == nil { return constants.Dimensions{} } return constants.Dimensions{ - Width: m.ctx.MainContentWidth - m.ctx.Styles.Section.ContainerStyle.GetHorizontalPadding(), - Height: m.ctx.MainContentHeight, + Width: m.Ctx.MainContentWidth - m.Ctx.Styles.Section.ContainerStyle.GetHorizontalPadding(), + Height: m.Ctx.MainContentHeight, } } func (m *Model) UpdateProgramContext(ctx *context.ProgramContext) { oldDimensions := m.GetDimensions() - m.ctx = ctx + m.Ctx = ctx newDimensions := m.GetDimensions() tableDimensions := constants.Dimensions{ Height: newDimensions.Height, Width: newDimensions.Width, } - m.table.SetDimensions(tableDimensions) - m.table.UpdateProgramContext(ctx) + m.Table.SetDimensions(tableDimensions) + m.Table.UpdateProgramContext(ctx) if oldDimensions.Height != newDimensions.Height || oldDimensions.Width != newDimensions.Width { - m.table.SyncViewPortContent() + m.Table.SyncViewPortContent() } } + +func (m *Model) GetItemSingularForm() string { + return "Branch" +} + +func (m *Model) GetItemPluralForm() string { + return "Branches" +} + +func (m *Model) GetTotalCount() *int { + if m.IsLoading() { + return nil + } + + return utils.IntPtr(len(m.Branches)) +} diff --git a/ui/ui.go b/ui/ui.go index aec92638..e6c8be99 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -41,7 +41,7 @@ type Model struct { issueSidebar issuesidebar.Model currSectionId int footer footer.Model - repo reposection.Model + repo section.Section prs []section.Section issues []section.Section tabs tabs.Model @@ -54,7 +54,7 @@ func NewModel(repoPath *string, configPath string) Model { taskSpinner := spinner.Model{Spinner: spinner.Dot} m := Model{ keys: keys.Keys, - currSectionId: 1, + currSectionId: 0, sidebar: sidebar.NewModel(), taskSpinner: taskSpinner, tasks: map[string]context.Task{}, @@ -288,7 +288,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, keys.BranchKeys.ViewPRs): m.ctx.View = m.switchSelectedView() m.syncMainContentWidth() - m.setCurrSectionId(1) + m.setCurrSectionId(m.getCurrentViewDefaultSection()) m.tabs.UpdateSectionsConfigs(&m.ctx) currSections := m.getCurrentViewSections() @@ -365,7 +365,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, keys.PRKeys.ViewIssues): m.ctx.View = m.switchSelectedView() m.syncMainContentWidth() - m.setCurrSectionId(1) + m.setCurrSectionId(m.getCurrentViewDefaultSection()) m.tabs.UpdateSectionsConfigs(&m.ctx) currSections := m.getCurrentViewSections() @@ -419,7 +419,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, keys.IssueKeys.ViewPRs): m.ctx.View = m.switchSelectedView() m.syncMainContentWidth() - m.setCurrSectionId(1) + m.setCurrSectionId(m.getCurrentViewDefaultSection()) m.tabs.UpdateSectionsConfigs(&m.ctx) currSections := m.getCurrentViewSections() @@ -621,7 +621,6 @@ func (m *Model) syncProgramContext() { for _, section := range m.getCurrentViewSections() { section.UpdateProgramContext(&m.ctx) } - m.repo.UpdateProgramContext(&m.ctx) m.footer.UpdateProgramContext(&m.ctx) m.sidebar.UpdateProgramContext(&m.ctx) m.prSidebar.UpdateProgramContext(&m.ctx) @@ -693,7 +692,8 @@ func (m *Model) syncSidebar() { func (m *Model) fetchAllViewSections() ([]section.Section, tea.Cmd) { if m.ctx.View == config.RepoView { var cmd tea.Cmd - m.repo, cmd = reposection.FetchAllBranches(m.ctx) + s, cmd := reposection.FetchAllBranches(m.ctx) + m.repo = &s return nil, cmd } else if m.ctx.View == config.PRsView { return prssection.FetchAllSections(m.ctx) @@ -704,7 +704,7 @@ func (m *Model) fetchAllViewSections() ([]section.Section, tea.Cmd) { func (m *Model) getCurrentViewSections() []section.Section { if m.ctx.View == config.RepoView { - return []section.Section{} + return []section.Section{m.repo} } else if m.ctx.View == config.PRsView { return m.prs } else { @@ -712,6 +712,16 @@ func (m *Model) getCurrentViewSections() []section.Section { } } +func (m *Model) getCurrentViewDefaultSection() int { + if m.ctx.View == config.RepoView { + return 0 + } else if m.ctx.View == config.PRsView { + return 1 + } else { + return 1 + } +} + func (m *Model) setCurrentViewSections(newSections []section.Section) { if newSections == nil { return From 6b3209034857a6e44df6a68b6491cbdeb6181f0b Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Fri, 23 Aug 2024 16:55:40 +0300 Subject: [PATCH 4/8] refactor: move read prs to commands.go --- ui/components/reposection/commands.go | 83 ++++++++++++++++++++++ ui/components/reposection/reposection.go | 90 ++---------------------- ui/components/section/section.go | 4 ++ 3 files changed, 94 insertions(+), 83 deletions(-) create mode 100644 ui/components/reposection/commands.go diff --git a/ui/components/reposection/commands.go b/ui/components/reposection/commands.go new file mode 100644 index 00000000..57b5c4a8 --- /dev/null +++ b/ui/components/reposection/commands.go @@ -0,0 +1,83 @@ +package reposection + +import ( + "fmt" + "time" + + tea "github.com/charmbracelet/bubbletea" + + "github.com/dlvhdr/gh-dash/v4/data" + "github.com/dlvhdr/gh-dash/v4/git" + "github.com/dlvhdr/gh-dash/v4/ui/constants" + "github.com/dlvhdr/gh-dash/v4/ui/context" +) + +type UpdatePRMsg struct { + PrNumber int + IsClosed *bool + NewComment *data.Comment + ReadyForReview *bool + IsMerged *bool + AddedAssignees *data.Assignees + RemovedAssignees *data.Assignees +} + +type repoMsg struct { + repo *git.Repo + err error +} + +func (m *Model) readRepoCmd(taskId string) tea.Cmd { + return func() tea.Msg { + repo, err := git.GetRepo(*m.Ctx.RepoPath) + return constants.TaskFinishedMsg{ + SectionId: 0, + SectionType: SectionType, + TaskId: taskId, + Msg: repoMsg{repo: repo}, + Err: err, + } + } +} + +func (m *Model) fetchPRsCmd() []tea.Cmd { + prsTaskId := fmt.Sprintf("fetching_pr_branches_%d", time.Now().Unix()) + cmds := make([]tea.Cmd, 0) + task := context.Task{ + Id: prsTaskId, + StartText: "Fetching PRs for your branches", + FinishedText: "PRs for your branches have been fetched", + State: context.TaskStart, + Error: nil, + } + cmds = append(cmds, m.Ctx.StartTask(task)) + cmds = append(cmds, func() tea.Msg { + limit := m.Config.Limit + if limit == nil { + limit = &m.Ctx.Config.Defaults.PrsLimit + } + res, err := data.FetchPullRequests("author:@me", *limit, nil) + // TODO: enrich with branches only for section with branches + if err != nil { + return constants.TaskFinishedMsg{ + SectionId: 0, + SectionType: SectionType, + TaskId: prsTaskId, + Err: err, + } + } + + return constants.TaskFinishedMsg{ + SectionId: 0, + SectionType: SectionType, + TaskId: prsTaskId, + Msg: SectionPullRequestsFetchedMsg{ + Prs: res.Prs, + TotalCount: res.TotalCount, + PageInfo: res.PageInfo, + TaskId: prsTaskId, + }, + } + }) + return cmds +} diff --git a/ui/components/reposection/reposection.go b/ui/components/reposection/reposection.go index f44ca524..bfe2cba1 100644 --- a/ui/components/reposection/reposection.go +++ b/ui/components/reposection/reposection.go @@ -421,50 +421,12 @@ func (m *Model) FetchNextPageSectionRows() []tea.Cmd { cmds = append(cmds, bCmd) } - prsTaskId := fmt.Sprintf("fetching_pr_branches", time.Now().Unix()) - task := context.Task{ - Id: prsTaskId, - StartText: "Fetching PRs for your branches", - FinishedText: "PRs for your branches have been fetched", - State: context.TaskStart, - Error: nil, - } - startCmd := m.Ctx.StartTask(task) - cmds = append(cmds, startCmd) - var repoCmd tea.Cmd if m.Ctx.RepoPath != nil { - repoCmd = m.makeRepoCmd(branchesTaskId) - } - fetchCmd := func() tea.Msg { - limit := m.Config.Limit - if limit == nil { - limit = &m.Ctx.Config.Defaults.PrsLimit - } - res, err := data.FetchPullRequests("author:@me", *limit, nil) - // TODO: enrich with branches only for section with branches - if err != nil { - return constants.TaskFinishedMsg{ - SectionId: 0, - SectionType: SectionType, - TaskId: prsTaskId, - Err: err, - } - } - - return constants.TaskFinishedMsg{ - SectionId: 0, - SectionType: SectionType, - TaskId: prsTaskId, - Msg: SectionPullRequestsFetchedMsg{ - Prs: res.Prs, - TotalCount: res.TotalCount, - PageInfo: res.PageInfo, - TaskId: prsTaskId, - }, - } + repoCmd = m.readRepoCmd(branchesTaskId) + cmds = append(cmds, repoCmd) } - cmds = append(cmds, fetchCmd, repoCmd) + cmds = append(cmds, m.fetchPRsCmd()...) m.Table.SetIsLoading(true) cmds = append(cmds, m.Table.StartLoadingSpinner()) @@ -476,23 +438,8 @@ func (m *Model) ResetRows() { m.Prs = nil } -type repoMsg struct { - repo *git.Repo - err error -} - -func openRepoCmd(dir string) tea.Cmd { - return func() tea.Msg { - repo, err := git.GetRepo(dir) - return repoMsg{repo: repo, err: err} - } -} - func FetchAllBranches(ctx context.ProgramContext) (Model, tea.Cmd) { cmds := make([]tea.Cmd, 0) - if ctx.RepoPath != nil { - cmds = append(cmds, openRepoCmd(*ctx.RepoPath)) - } t := config.RepoView cfg := config.PrsSectionConfig{ @@ -507,21 +454,15 @@ func FetchAllBranches(ctx context.ProgramContext) (Model, tea.Cmd) { cfg, time.Now(), ) + + if ctx.RepoPath != nil { + cmds = append(cmds, m.readRepoCmd("fetch_all_branches_")) + } cmds = append(cmds, m.FetchNextPageSectionRows()...) return m, tea.Batch(cmds...) } -type UpdatePRMsg struct { - PrNumber int - IsClosed *bool - NewComment *data.Comment - ReadyForReview *bool - IsMerged *bool - AddedAssignees *data.Assignees - RemovedAssignees *data.Assignees -} - func addAssignees(assignees, addedAssignees []data.Assignee) []data.Assignee { newAssignees := assignees for _, assignee := range addedAssignees { @@ -555,23 +496,6 @@ func assigneesContains(assignees []data.Assignee, assignee data.Assignee) bool { return false } -func (m *Model) IsLoading() bool { - return m.Table.IsLoading() -} - -func (m *Model) makeRepoCmd(taskId string) tea.Cmd { - return func() tea.Msg { - repo, err := git.GetRepo(*m.Ctx.RepoPath) - return constants.TaskFinishedMsg{ - SectionId: 0, - SectionType: SectionType, - TaskId: taskId, - Msg: repoMsg{repo: repo}, - Err: err, - } - } -} - func (m Model) GetDimensions() constants.Dimensions { if m.Ctx == nil { return constants.Dimensions{} diff --git a/ui/components/section/section.go b/ui/components/section/section.go index 163da268..db49f5b0 100644 --- a/ui/components/section/section.go +++ b/ui/components/section/section.go @@ -344,6 +344,10 @@ func (m *BaseModel) UpdateTotalItemsCount(count int) { m.Table.UpdateTotalItemsCount(count) } +func (m *BaseModel) IsLoading() bool { + return m.Table.IsLoading() +} + func (m *BaseModel) GetPagerContent() string { pagerContent := "" if m.TotalCount > 0 { From 12b75b845c72f19464585cccdfbf722c418af779 Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Fri, 23 Aug 2024 17:02:02 +0300 Subject: [PATCH 5/8] refactor: move read branches to commands.go --- ui/components/reposection/checkout.go | 2 +- ui/components/reposection/commands.go | 25 +++- ui/components/reposection/delete.go | 2 +- ui/components/reposection/reposection.go | 145 ++--------------------- 4 files changed, 31 insertions(+), 143 deletions(-) diff --git a/ui/components/reposection/checkout.go b/ui/components/reposection/checkout.go index 28ead9a1..083a685b 100644 --- a/ui/components/reposection/checkout.go +++ b/ui/components/reposection/checkout.go @@ -13,7 +13,7 @@ import ( ) func (m *Model) checkout() (tea.Cmd, error) { - b := m.GetCurrBranch() + b := m.getCurrBranch() taskId := fmt.Sprintf("checkout_%s_%d", b.Data.Name, time.Now().Unix()) task := context.Task{ diff --git a/ui/components/reposection/commands.go b/ui/components/reposection/commands.go index 57b5c4a8..245c1037 100644 --- a/ui/components/reposection/commands.go +++ b/ui/components/reposection/commands.go @@ -27,17 +27,34 @@ type repoMsg struct { err error } -func (m *Model) readRepoCmd(taskId string) tea.Cmd { - return func() tea.Msg { +func (m *Model) readRepoCmd() []tea.Cmd { + cmds := make([]tea.Cmd, 0) + branchesTaskId := fmt.Sprintf("fetching_branches_%d", time.Now().Unix()) + if m.Ctx.RepoPath != nil { + branchesTask := context.Task{ + Id: branchesTaskId, + StartText: "Reading local branches", + FinishedText: fmt.Sprintf( + `Read branches successfully for "%s"`, + *m.Ctx.RepoPath, + ), + State: context.TaskStart, + Error: nil, + } + bCmd := m.Ctx.StartTask(branchesTask) + cmds = append(cmds, bCmd) + } + cmds = append(cmds, func() tea.Msg { repo, err := git.GetRepo(*m.Ctx.RepoPath) return constants.TaskFinishedMsg{ SectionId: 0, SectionType: SectionType, - TaskId: taskId, + TaskId: branchesTaskId, Msg: repoMsg{repo: repo}, Err: err, } - } + }) + return cmds } func (m *Model) fetchPRsCmd() []tea.Cmd { diff --git a/ui/components/reposection/delete.go b/ui/components/reposection/delete.go index 426d2ef8..478fd0c9 100644 --- a/ui/components/reposection/delete.go +++ b/ui/components/reposection/delete.go @@ -13,7 +13,7 @@ import ( ) func (m *Model) delete() (tea.Cmd, error) { - b := m.GetCurrBranch() + b := m.getCurrBranch() taskId := fmt.Sprintf("delete_%s_%d", b.Data.Name, time.Now().Unix()) task := context.Task{ diff --git a/ui/components/reposection/reposection.go b/ui/components/reposection/reposection.go index bfe2cba1..cc0ab9ba 100644 --- a/ui/components/reposection/reposection.go +++ b/ui/components/reposection/reposection.go @@ -1,7 +1,6 @@ package reposection import ( - "fmt" "slices" "strings" "time" @@ -66,35 +65,6 @@ func (m *Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { case tea.KeyMsg: - // if m.IsPromptConfirmationFocused() { - // switch { - // case msg.Type == tea.KeyCtrlC, msg.Type == tea.KeyEsc: - // m.PromptConfirmationBox.Reset() - // cmd = m.SetIsPromptConfirmationShown(false) - // return &m, cmd - // - // case msg.Type == tea.KeyEnter: - // input := m.PromptConfirmationBox.Value() - // action := m.GetPromptConfirmationAction() - // if input == "Y" || input == "y" { - // switch action { - // case "delete": - // cmd, err = m.delete() - // if err != nil { - // m.Ctx.Error = err - // } - // } - // } - // - // m.PromptConfirmationBox.Reset() - // blinkCmd := m.SetIsPromptConfirmationShown(false) - // - // return &m, tea.Batch(cmd, blinkCmd) - // } - // - // break - // } - switch { case key.Matches(msg, keys.BranchKeys.Checkout): cmd, err = m.checkout() @@ -104,39 +74,6 @@ func (m *Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { } - case UpdatePRMsg: - for i, currPr := range m.Prs { - if currPr.Number == msg.PrNumber { - if msg.IsClosed != nil { - if *msg.IsClosed { - currPr.State = "CLOSED" - } else { - currPr.State = "OPEN" - } - } - if msg.NewComment != nil { - currPr.Comments.Nodes = append(currPr.Comments.Nodes, *msg.NewComment) - } - if msg.AddedAssignees != nil { - currPr.Assignees.Nodes = addAssignees(currPr.Assignees.Nodes, msg.AddedAssignees.Nodes) - } - if msg.RemovedAssignees != nil { - currPr.Assignees.Nodes = removeAssignees(currPr.Assignees.Nodes, msg.RemovedAssignees.Nodes) - } - if msg.ReadyForReview != nil && *msg.ReadyForReview { - currPr.IsDraft = false - } - if msg.IsMerged != nil && *msg.IsMerged { - currPr.State = "MERGED" - currPr.Mergeable = "" - } - m.Prs[i] = currPr - m.Table.SetIsLoading(false) - m.Table.SetRows(m.BuildRows()) - break - } - } - case repoMsg: m.repo = msg.repo m.Table.SetIsLoading(false) @@ -144,24 +81,10 @@ func (m *Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { m.Table.SetRows(m.BuildRows()) m.Table.ResetCurrItem() - case SectionPullRequestsFetchedMsg: - // if m.LastFetchTaskId == msg.TaskId { - // m.Prs = msg.Prs - // m.TotalCount = msg.TotalCount - // m.PageInfo = &msg.PageInfo - // m.Table.SetIsLoading(false) - // m.updateBranches() - // m.Table.SetRows(m.BuildRows()) - // m.Table.UpdateLastUpdated(time.Now()) - // m.UpdateTotalItemsCount(m.TotalCount) - // } } m.Table.SetRows(m.BuildRows()) - // prompt, promptCmd := m.PromptConfirmationBox.Update(msg) - // m.PromptConfirmationBox = prompt - table, tableCmd := m.Table.Update(msg) m.Table = table @@ -382,7 +305,7 @@ type SectionPullRequestsFetchedMsg struct { TaskId string } -func (m *Model) GetCurrBranch() *branch.Branch { +func (m *Model) getCurrBranch() *branch.Branch { if len(m.repo.Branches) == 0 { return nil } @@ -404,29 +327,10 @@ func (m *Model) FetchNextPageSectionRows() []tea.Cmd { } var cmds []tea.Cmd - - branchesTaskId := fmt.Sprintf("fetching_branches_%d", time.Now().Unix()) - if m.Ctx.RepoPath != nil { - branchesTask := context.Task{ - Id: branchesTaskId, - StartText: "Reading local branches", - FinishedText: fmt.Sprintf( - `Read branches successfully for "%s"`, - *m.Ctx.RepoPath, - ), - State: context.TaskStart, - Error: nil, - } - bCmd := m.Ctx.StartTask(branchesTask) - cmds = append(cmds, bCmd) - } - - var repoCmd tea.Cmd if m.Ctx.RepoPath != nil { - repoCmd = m.readRepoCmd(branchesTaskId) - cmds = append(cmds, repoCmd) + cmds = append(cmds, m.readRepoCmd()...) + cmds = append(cmds, m.fetchPRsCmd()...) } - cmds = append(cmds, m.fetchPRsCmd()...) m.Table.SetIsLoading(true) cmds = append(cmds, m.Table.StartLoadingSpinner()) @@ -434,10 +338,6 @@ func (m *Model) FetchNextPageSectionRows() []tea.Cmd { return cmds } -func (m *Model) ResetRows() { - m.Prs = nil -} - func FetchAllBranches(ctx context.ProgramContext) (Model, tea.Cmd) { cmds := make([]tea.Cmd, 0) @@ -456,46 +356,13 @@ func FetchAllBranches(ctx context.ProgramContext) (Model, tea.Cmd) { ) if ctx.RepoPath != nil { - cmds = append(cmds, m.readRepoCmd("fetch_all_branches_")) + cmds = append(cmds, m.readRepoCmd()...) } cmds = append(cmds, m.FetchNextPageSectionRows()...) return m, tea.Batch(cmds...) } -func addAssignees(assignees, addedAssignees []data.Assignee) []data.Assignee { - newAssignees := assignees - for _, assignee := range addedAssignees { - if !assigneesContains(newAssignees, assignee) { - newAssignees = append(newAssignees, assignee) - } - } - - return newAssignees -} - -func removeAssignees( - assignees, removedAssignees []data.Assignee, -) []data.Assignee { - newAssignees := []data.Assignee{} - for _, assignee := range assignees { - if !assigneesContains(removedAssignees, assignee) { - newAssignees = append(newAssignees, assignee) - } - } - - return newAssignees -} - -func assigneesContains(assignees []data.Assignee, assignee data.Assignee) bool { - for _, a := range assignees { - if assignee == a { - return true - } - } - return false -} - func (m Model) GetDimensions() constants.Dimensions { if m.Ctx == nil { return constants.Dimensions{} @@ -523,6 +390,10 @@ func (m *Model) UpdateProgramContext(ctx *context.ProgramContext) { } } +func (m *Model) ResetRows() { + m.Prs = nil +} + func (m *Model) GetItemSingularForm() string { return "Branch" } From d6a82d2df4a58aa89b275935d1cdae0c37a8056b Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Fri, 23 Aug 2024 17:30:29 +0300 Subject: [PATCH 6/8] fix(branches): don't crash on open --- ui/tasks.go | 37 +++++++++++++++++++++++++++++++++++++ ui/ui.go | 10 +--------- 2 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 ui/tasks.go diff --git a/ui/tasks.go b/ui/tasks.go new file mode 100644 index 00000000..475af422 --- /dev/null +++ b/ui/tasks.go @@ -0,0 +1,37 @@ +package ui + +import ( + "errors" + "fmt" + "os" + "reflect" + "time" + + tea "github.com/charmbracelet/bubbletea" + "github.com/cli/go-gh/v2/pkg/browser" + + "github.com/dlvhdr/gh-dash/v4/ui/constants" + "github.com/dlvhdr/gh-dash/v4/ui/context" +) + +func (m *Model) openBrowser() tea.Cmd { + taskId := fmt.Sprintf("open_browser_%d", time.Now().Unix()) + task := context.Task{ + Id: taskId, + StartText: "Opening in browser", + FinishedText: "Opened in browser", + State: context.TaskStart, + Error: nil, + } + startCmd := m.ctx.StartTask(task) + openCmd := func() tea.Msg { + b := browser.New("", os.Stdout, os.Stdin) + currRow := m.getCurrRowData() + if reflect.ValueOf(currRow).IsNil() { + return constants.TaskFinishedMsg{TaskId: taskId, Err: errors.New("Current selection doesn't have a URL")} + } + err := b.Browse(currRow.GetUrl()) + return constants.TaskFinishedMsg{TaskId: taskId, Err: err} + } + return tea.Batch(startCmd, openCmd) +} diff --git a/ui/ui.go b/ui/ui.go index e6c8be99..c062f8b4 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -13,7 +13,6 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" log "github.com/charmbracelet/log" - "github.com/cli/go-gh/v2/pkg/browser" "github.com/dlvhdr/gh-dash/v4/config" "github.com/dlvhdr/gh-dash/v4/data" @@ -214,14 +213,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.syncMainContentWidth() case key.Matches(msg, m.keys.OpenGithub): - currRow := m.getCurrRowData() - b := browser.New("", os.Stdout, os.Stdin) - if currRow != nil { - err := b.Browse(currRow.GetUrl()) - if err != nil { - log.Fatal(err) - } - } + cmds = append(cmds, m.openBrowser()) case key.Matches(msg, m.keys.Refresh): currSection.ResetFilters() From 1981bb6e16f50f80cb79d5c2b878d2ef480df7ac Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Fri, 23 Aug 2024 18:19:34 +0300 Subject: [PATCH 7/8] fix: default view on startup --- ui/ui.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/ui.go b/ui/ui.go index c062f8b4..9ecaf12d 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -52,11 +52,10 @@ type Model struct { func NewModel(repoPath *string, configPath string) Model { taskSpinner := spinner.Model{Spinner: spinner.Dot} m := Model{ - keys: keys.Keys, - currSectionId: 0, - sidebar: sidebar.NewModel(), - taskSpinner: taskSpinner, - tasks: map[string]context.Task{}, + keys: keys.Keys, + sidebar: sidebar.NewModel(), + taskSpinner: taskSpinner, + tasks: map[string]context.Task{}, } m.ctx = context.ProgramContext{ @@ -71,6 +70,8 @@ func NewModel(repoPath *string, configPath string) Model { return m.taskSpinner.Tick }, } + + m.currSectionId = m.getCurrentViewDefaultSection() m.taskSpinner.Style = lipgloss.NewStyle(). Background(m.ctx.Theme.SelectedBackground) From 404c49e28987231dec97405e38e26c4a40b65ab0 Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Fri, 23 Aug 2024 18:20:18 +0300 Subject: [PATCH 8/8] chore: remove log --- ui/components/table/table.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/components/table/table.go b/ui/components/table/table.go index 4dd73155..a527717b 100644 --- a/ui/components/table/table.go +++ b/ui/components/table/table.go @@ -7,7 +7,6 @@ import ( "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/charmbracelet/log" "github.com/dlvhdr/gh-dash/v4/ui/common" "github.com/dlvhdr/gh-dash/v4/ui/components/listviewport" @@ -55,7 +54,6 @@ func NewModel( if ctx.Config.Theme.Ui.Table.ShowSeparator { itemHeight += 1 } - log.Debug("ctx", "itemHeight", itemHeight) loadingSpinner := spinner.New() loadingSpinner.Spinner = spinner.Dot