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	case refs.RefMsg:
 89		b.state = treePage
 90	}
 91	m, cmd := b.boxes[b.state].Update(msg)
 92	b.boxes[b.state] = m
 93	if cmd != nil {
 94		cmds = append(cmds, cmd)
 95	}
 96	return b, tea.Batch(cmds...)
 97}
 98
 99func (b *Bubble) Help() []types.HelpEntry {
100	h := []types.HelpEntry{}
101	h = append(h, b.boxes[b.state].(types.HelpableBubble).Help()...)
102	if b.repo.Name() != "config" {
103		h = append(h, types.HelpEntry{"r", "readme"})
104		h = append(h, types.HelpEntry{"f", "files"})
105		h = append(h, types.HelpEntry{"c", "commits"})
106		h = append(h, types.HelpEntry{"b", "branches/tags"})
107	}
108	return h
109}
110
111func (b *Bubble) Reference() plumbing.ReferenceName {
112	return b.repo.GetReference().Name()
113}
114
115func (b *Bubble) headerView() string {
116	// TODO better header, tabs?
117	return ""
118}
119
120func (b *Bubble) View() string {
121	header := b.headerView()
122	return header + b.boxes[b.state].View()
123}
124
125func (b *Bubble) setupCmd() tea.Msg {
126	cmds := make([]tea.Cmd, 0)
127	for _, bx := range b.boxes {
128		if bx != nil {
129			initCmd := bx.Init()
130			if initCmd != nil {
131				msg := initCmd()
132				switch msg := msg.(type) {
133				case types.ErrMsg:
134					return msg
135				}
136			}
137			cmds = append(cmds, initCmd)
138		}
139	}
140	return tea.Batch(cmds...)
141}