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}