bubble.go

  1package git
  2
  3import (
  4	tea "github.com/charmbracelet/bubbletea"
  5	"github.com/charmbracelet/lipgloss"
  6	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/about"
  7	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/log"
  8	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/refs"
  9	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/tree"
 10	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
 11	"github.com/charmbracelet/soft-serve/internal/tui/style"
 12	"github.com/go-git/go-git/v5/plumbing"
 13)
 14
 15const (
 16	repoNameMaxWidth = 32
 17)
 18
 19type pageState int
 20
 21const (
 22	aboutPage pageState = iota
 23	refsPage
 24	logPage
 25	treePage
 26)
 27
 28type Bubble struct {
 29	state        pageState
 30	repo         types.Repo
 31	height       int
 32	heightMargin int
 33	width        int
 34	widthMargin  int
 35	style        *style.Styles
 36	boxes        []tea.Model
 37}
 38
 39func NewBubble(repo types.Repo, styles *style.Styles, width, wm, height, hm int) *Bubble {
 40	b := &Bubble{
 41		repo:         repo,
 42		state:        aboutPage,
 43		width:        width,
 44		widthMargin:  wm,
 45		height:       height,
 46		heightMargin: hm,
 47		style:        styles,
 48		boxes:        make([]tea.Model, 4),
 49	}
 50	heightMargin := hm + lipgloss.Height(b.headerView())
 51	b.boxes[aboutPage] = about.NewBubble(repo, b.style, b.width, wm, b.height, heightMargin)
 52	b.boxes[refsPage] = refs.NewBubble(repo, b.style, b.width, wm, b.height, heightMargin)
 53	b.boxes[logPage] = log.NewBubble(repo, b.style, width, wm, height, heightMargin)
 54	b.boxes[treePage] = tree.NewBubble(repo, b.style, width, wm, height, heightMargin)
 55	return b
 56}
 57
 58func (b *Bubble) Init() tea.Cmd {
 59	return b.setupCmd
 60}
 61
 62func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 63	cmds := make([]tea.Cmd, 0)
 64	switch msg := msg.(type) {
 65	case tea.KeyMsg:
 66		if b.repo.Name() != "config" {
 67			switch msg.String() {
 68			case "r", "R":
 69				b.state = aboutPage
 70			case "b", "B":
 71				b.state = refsPage
 72			case "c", "C":
 73				b.state = logPage
 74			case "f", "F":
 75				b.state = treePage
 76			}
 77		}
 78	case tea.WindowSizeMsg:
 79		b.width = msg.Width
 80		b.height = msg.Height
 81		for i, bx := range b.boxes {
 82			m, cmd := bx.Update(msg)
 83			b.boxes[i] = m
 84			if cmd != nil {
 85				cmds = append(cmds, cmd)
 86			}
 87		}
 88	}
 89	m, cmd := b.boxes[b.state].Update(msg)
 90	b.boxes[b.state] = m
 91	if cmd != nil {
 92		cmds = append(cmds, cmd)
 93	}
 94
 95	switch msg := msg.(type) {
 96	case tea.KeyMsg:
 97		switch msg.String() {
 98		case "enter":
 99			if b.state == refsPage {
100				b.state = treePage
101				cmds = append(cmds, b.boxes[b.state].Init())
102			}
103		}
104	}
105	return b, tea.Batch(cmds...)
106}
107
108func (b *Bubble) Help() []types.HelpEntry {
109	h := []types.HelpEntry{}
110	h = append(h, b.boxes[b.state].(types.HelpableBubble).Help()...)
111	if b.repo.Name() != "config" {
112		h = append(h, types.HelpEntry{"r", "readme"})
113		h = append(h, types.HelpEntry{"f", "files"})
114		h = append(h, types.HelpEntry{"c", "commits"})
115		h = append(h, types.HelpEntry{"b", "branches/tags"})
116	}
117	return h
118}
119
120func (b *Bubble) Reference() plumbing.ReferenceName {
121	return b.repo.GetReference().Name()
122}
123
124func (b *Bubble) headerView() string {
125	// TODO better header, tabs?
126	return ""
127}
128
129func (b *Bubble) View() string {
130	header := b.headerView()
131	return header + b.boxes[b.state].View()
132}
133
134func (b *Bubble) setupCmd() tea.Msg {
135	cmds := make([]tea.Cmd, 0)
136	for _, bx := range b.boxes {
137		if bx != nil {
138			initCmd := bx.Init()
139			if initCmd != nil {
140				msg := initCmd()
141				switch msg := msg.(type) {
142				case types.ErrMsg:
143					return msg
144				}
145			}
146			cmds = append(cmds, initCmd)
147		}
148	}
149	return tea.Batch(cmds...)
150}