log.go

  1package repo
  2
  3import (
  4	"fmt"
  5
  6	"github.com/charmbracelet/bubbles/list"
  7	tea "github.com/charmbracelet/bubbletea"
  8	ggit "github.com/charmbracelet/soft-serve/git"
  9	"github.com/charmbracelet/soft-serve/ui/common"
 10	"github.com/charmbracelet/soft-serve/ui/components/selector"
 11	"github.com/charmbracelet/soft-serve/ui/components/viewport"
 12	"github.com/charmbracelet/soft-serve/ui/git"
 13)
 14
 15type view int
 16
 17const (
 18	logView view = iota
 19	commitView
 20)
 21
 22type LogCountMsg int64
 23
 24type LogItemsMsg []list.Item
 25
 26type Log struct {
 27	common     common.Common
 28	selector   *selector.Selector
 29	vp         *viewport.Viewport
 30	activeView view
 31	repo       git.GitRepo
 32	ref        *ggit.Reference
 33	count      int64
 34	nextPage   int
 35}
 36
 37func NewLog(common common.Common) *Log {
 38	l := &Log{
 39		common:     common,
 40		vp:         viewport.New(),
 41		activeView: logView,
 42	}
 43	selector := selector.New(common, []selector.IdentifiableItem{}, LogItemDelegate{common.Styles})
 44	selector.SetShowFilter(false)
 45	selector.SetShowHelp(false)
 46	selector.SetShowPagination(false)
 47	selector.SetShowStatusBar(false)
 48	selector.SetShowTitle(false)
 49	selector.SetFilteringEnabled(false)
 50	selector.DisableQuitKeybindings()
 51	selector.KeyMap.NextPage = common.KeyMap.NextPage
 52	selector.KeyMap.PrevPage = common.KeyMap.PrevPage
 53	l.selector = selector
 54	return l
 55}
 56
 57func (l *Log) SetSize(width, height int) {
 58	l.common.SetSize(width, height)
 59	l.selector.SetSize(width, height)
 60	l.vp.SetSize(width, height)
 61}
 62
 63func (l *Log) Init() tea.Cmd {
 64	cmds := make([]tea.Cmd, 0)
 65	cmds = append(cmds, l.updateCommitsCmd)
 66	return tea.Batch(cmds...)
 67}
 68
 69func (l *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 70	cmds := make([]tea.Cmd, 0)
 71	switch msg := msg.(type) {
 72	case RepoMsg:
 73		l.count = 0
 74		l.selector.Select(0)
 75		l.nextPage = 0
 76		l.repo = git.GitRepo(msg)
 77	case RefMsg:
 78		l.ref = msg
 79		l.count = 0
 80		cmds = append(cmds, l.countCommitsCmd)
 81	case LogCountMsg:
 82		l.count = int64(msg)
 83	case LogItemsMsg:
 84		cmds = append(cmds, l.selector.SetItems(msg))
 85		l.selector.SetPage(l.nextPage)
 86		l.SetSize(l.common.Width, l.common.Height)
 87	case tea.KeyMsg, tea.MouseMsg:
 88		// This is a hack for loading commits on demand based on list.Pagination.
 89		if l.activeView == logView {
 90			curPage := l.selector.Page()
 91			s, cmd := l.selector.Update(msg)
 92			m := s.(*selector.Selector)
 93			l.selector = m
 94			if m.Page() != curPage {
 95				l.nextPage = m.Page()
 96				l.selector.SetPage(curPage)
 97				cmds = append(cmds, l.updateCommitsCmd)
 98			}
 99			cmds = append(cmds, cmd)
100		}
101	}
102	switch l.activeView {
103	case commitView:
104		vp, cmd := l.vp.Update(msg)
105		l.vp = vp.(*viewport.Viewport)
106		if cmd != nil {
107			cmds = append(cmds, cmd)
108		}
109	}
110	return l, tea.Batch(cmds...)
111}
112
113func (l *Log) View() string {
114	switch l.activeView {
115	case logView:
116		return l.selector.View()
117	case commitView:
118		return l.vp.View()
119	default:
120		return ""
121	}
122}
123
124func (l *Log) StatusBarInfo() string {
125	switch l.activeView {
126	case logView:
127		// We're using l.nextPage instead of l.selector.Paginator.Page because
128		// of the paginator hack above.
129		return fmt.Sprintf("%d/%d", l.nextPage+1, l.selector.TotalPages())
130	default:
131		return ""
132	}
133}
134
135func (l *Log) countCommitsCmd() tea.Msg {
136	count, err := l.repo.CountCommits(l.ref)
137	if err != nil {
138		return common.ErrorMsg(err)
139	}
140	return LogCountMsg(count)
141}
142
143func (l *Log) updateCommitsCmd() tea.Msg {
144	count := l.count
145	if l.count == 0 {
146		switch msg := l.countCommitsCmd().(type) {
147		case common.ErrorMsg:
148			return msg
149		case LogCountMsg:
150			count = int64(msg)
151		}
152	}
153	items := make([]list.Item, count)
154	page := l.nextPage
155	limit := l.selector.PerPage()
156	skip := page * limit
157	// CommitsByPage pages start at 1
158	cc, err := l.repo.CommitsByPage(l.ref, page+1, limit)
159	if err != nil {
160		return common.ErrorMsg(err)
161	}
162	for i, c := range cc {
163		idx := i + skip
164		if int64(idx) >= count {
165			break
166		}
167		items[idx] = LogItem{c}
168	}
169	return LogItemsMsg(items)
170}