1package tui
  2
  3import (
  4	"bytes"
  5	"fmt"
  6	"text/template"
  7
  8	tea "github.com/charmbracelet/bubbletea"
  9	"github.com/charmbracelet/lipgloss"
 10	gitypes "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
 11	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/repo"
 12	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/selection"
 13	gm "github.com/charmbracelet/wish/git"
 14)
 15
 16type errMsg struct{ err error }
 17
 18func (e errMsg) Error() string {
 19	return e.err.Error()
 20}
 21
 22func (b *Bubble) setupCmd() tea.Msg {
 23	if b.config == nil || b.config.Source == nil {
 24		return errMsg{err: fmt.Errorf("config not set")}
 25	}
 26	mes, err := b.menuEntriesFromSource()
 27	if err != nil {
 28		return errMsg{err}
 29	}
 30	if len(mes) == 0 {
 31		return errMsg{fmt.Errorf("no repos found")}
 32	}
 33	b.repoMenu = mes
 34	rs := make([]string, 0)
 35	for _, m := range mes {
 36		rs = append(rs, m.Name)
 37	}
 38	b.repoSelect = selection.NewBubble(rs, b.styles)
 39	b.boxes[0] = b.repoSelect
 40
 41	// Jump to an initial repo
 42	ir := -1
 43	if b.initialRepo != "" {
 44		for i, me := range b.repoMenu {
 45			if me.Repo == b.initialRepo {
 46				ir = i
 47			}
 48		}
 49	}
 50	if ir == -1 {
 51		b.boxes[1] = b.repoMenu[0].bubble
 52		b.activeBox = 0
 53	} else {
 54		b.boxes[1] = b.repoMenu[ir].bubble
 55		b.repoSelect.SelectedItem = ir
 56		b.activeBox = 1
 57	}
 58
 59	b.state = loadedState
 60	return nil
 61}
 62
 63func (b *Bubble) menuEntriesFromSource() ([]MenuEntry, error) {
 64	mes := make([]MenuEntry, 0)
 65	for _, cr := range b.config.Repos {
 66		acc := b.config.AuthRepo(cr.Repo, b.session.PublicKey())
 67		if acc == gm.NoAccess && cr.Repo != "config" {
 68			continue
 69		}
 70		me, err := b.newMenuEntry(cr.Name, cr.Repo)
 71		if err != nil {
 72			return nil, err
 73		}
 74		mes = append(mes, me)
 75	}
 76	for _, r := range b.config.Source.AllRepos() {
 77		var found bool
 78		rn := r.Name()
 79		for _, me := range mes {
 80			if me.Repo == rn {
 81				found = true
 82			}
 83		}
 84		if !found {
 85			acc := b.config.AuthRepo(rn, b.session.PublicKey())
 86			if acc == gm.NoAccess {
 87				continue
 88			}
 89			me, err := b.newMenuEntry(rn, rn)
 90			if err != nil {
 91				return nil, err
 92			}
 93			mes = append(mes, me)
 94		}
 95	}
 96	return mes, nil
 97}
 98
 99func (b *Bubble) newMenuEntry(name string, rn string) (MenuEntry, error) {
100	me := MenuEntry{Name: name, Repo: rn}
101	r, err := b.config.Source.GetRepo(rn)
102	if err != nil {
103		return me, err
104	}
105	if rn == "config" {
106		md, err := templatize(r.Readme, b.config)
107		if err != nil {
108			return me, err
109		}
110		r.Readme = md
111	}
112	boxLeftWidth := b.styles.Menu.GetWidth() + b.styles.Menu.GetHorizontalFrameSize()
113	// TODO: also send this along with a tea.WindowSizeMsg
114	var heightMargin = lipgloss.Height(b.headerView()) +
115		lipgloss.Height(b.footerView()) +
116		b.styles.RepoBody.GetVerticalFrameSize() +
117		b.styles.App.GetVerticalMargins()
118	rb := repo.NewBubble(r, b.config.Host, b.config.Port, b.styles, b.width, boxLeftWidth, b.height, heightMargin)
119	initCmd := rb.Init()
120	msg := initCmd()
121	switch msg := msg.(type) {
122	case gitypes.ErrMsg:
123		return me, fmt.Errorf("missing %s: %s", me.Repo, msg.Err.Error())
124	}
125	me.bubble = rb
126	return me, nil
127}
128
129func templatize(mdt string, tmpl interface{}) (string, error) {
130	t, err := template.New("readme").Parse(mdt)
131	if err != nil {
132		return "", err
133	}
134	buf := &bytes.Buffer{}
135	err = t.Execute(buf, tmpl)
136	if err != nil {
137		return "", err
138	}
139	return buf.String(), nil
140}