1package tui
  2
  3import (
  4	tea "github.com/charmbracelet/bubbletea"
  5	"github.com/charmbracelet/lipgloss"
  6	"github.com/charmbracelet/soft-serve/internal/tui/style"
  7	"github.com/charmbracelet/soft-serve/pkg/git"
  8	"github.com/charmbracelet/soft-serve/pkg/tui/about"
  9	"github.com/charmbracelet/soft-serve/pkg/tui/common"
 10	"github.com/charmbracelet/soft-serve/pkg/tui/log"
 11	"github.com/charmbracelet/soft-serve/pkg/tui/refs"
 12	"github.com/charmbracelet/soft-serve/pkg/tui/tree"
 13)
 14
 15const (
 16	repoNameMaxWidth = 32
 17)
 18
 19type state int
 20
 21const (
 22	aboutState state = iota
 23	refsState
 24	logState
 25	treeState
 26)
 27
 28type Bubble struct {
 29	state        state
 30	repo         common.GitRepo
 31	height       int
 32	heightMargin int
 33	width        int
 34	widthMargin  int
 35	style        *style.Styles
 36	boxes        []tea.Model
 37	ref          *git.Reference
 38}
 39
 40func NewBubble(repo common.GitRepo, styles *style.Styles, width, wm, height, hm int) *Bubble {
 41	b := &Bubble{
 42		repo:         repo,
 43		state:        aboutState,
 44		width:        width,
 45		widthMargin:  wm,
 46		height:       height,
 47		heightMargin: hm,
 48		style:        styles,
 49		boxes:        make([]tea.Model, 4),
 50	}
 51	heightMargin := hm + lipgloss.Height(b.headerView())
 52	b.boxes[aboutState] = about.NewBubble(repo, b.style, b.width, wm, b.height, heightMargin)
 53	b.boxes[refsState] = refs.NewBubble(repo, b.style, b.width, wm, b.height, heightMargin)
 54	b.boxes[logState] = log.NewBubble(repo, b.style, width, wm, height, heightMargin)
 55	b.boxes[treeState] = tree.NewBubble(repo, b.style, width, wm, height, heightMargin)
 56	return b
 57}
 58
 59func (b *Bubble) Init() tea.Cmd {
 60	return b.setupCmd
 61}
 62
 63func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 64	cmds := make([]tea.Cmd, 0)
 65	switch msg := msg.(type) {
 66	case tea.KeyMsg:
 67		if b.repo.Name() != "config" {
 68			switch msg.String() {
 69			case "R":
 70				b.state = aboutState
 71			case "B":
 72				b.state = refsState
 73			case "C":
 74				b.state = logState
 75			case "F":
 76				b.state = treeState
 77			}
 78		}
 79	case tea.WindowSizeMsg:
 80		b.width = msg.Width
 81		b.height = msg.Height
 82		for i, bx := range b.boxes {
 83			m, cmd := bx.Update(msg)
 84			b.boxes[i] = m
 85			if cmd != nil {
 86				cmds = append(cmds, cmd)
 87			}
 88		}
 89	case refs.RefMsg:
 90		b.state = treeState
 91		b.ref = msg
 92		for i, bx := range b.boxes {
 93			m, cmd := bx.Update(msg)
 94			b.boxes[i] = m
 95			if cmd != nil {
 96				cmds = append(cmds, cmd)
 97			}
 98		}
 99	}
100	m, cmd := b.boxes[b.state].Update(msg)
101	b.boxes[b.state] = m
102	if cmd != nil {
103		cmds = append(cmds, cmd)
104	}
105	return b, tea.Batch(cmds...)
106}
107
108func (b *Bubble) Help() []common.HelpEntry {
109	h := []common.HelpEntry{}
110	h = append(h, b.boxes[b.state].(common.BubbleHelper).Help()...)
111	if b.repo.Name() != "config" {
112		h = append(h, common.HelpEntry{Key: "R", Value: "readme"})
113		h = append(h, common.HelpEntry{Key: "F", Value: "files"})
114		h = append(h, common.HelpEntry{Key: "C", Value: "commits"})
115		h = append(h, common.HelpEntry{Key: "B", Value: "branches"})
116	}
117	return h
118}
119
120func (b *Bubble) Reference() *git.Reference {
121	return b.ref
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	head, err := b.repo.HEAD()
136	if err != nil {
137		return common.ErrMsg{Err: err}
138	}
139	b.ref = head
140	cmds := make([]tea.Cmd, 0)
141	for _, bx := range b.boxes {
142		if bx != nil {
143			initCmd := bx.Init()
144			if initCmd != nil {
145				msg := initCmd()
146				switch msg := msg.(type) {
147				case common.ErrMsg:
148					return msg
149				}
150			}
151			cmds = append(cmds, initCmd)
152		}
153	}
154	return tea.Batch(cmds...)
155}