1package repo
  2
  3import (
  4	"fmt"
  5	"path/filepath"
  6
  7	"github.com/charmbracelet/bubbles/v2/key"
  8	"github.com/charmbracelet/bubbles/v2/spinner"
  9	tea "github.com/charmbracelet/bubbletea/v2"
 10	"github.com/charmbracelet/soft-serve/pkg/backend"
 11	"github.com/charmbracelet/soft-serve/pkg/proto"
 12	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 13	"github.com/charmbracelet/soft-serve/pkg/ui/components/code"
 14)
 15
 16// ReadmeMsg is a message sent when the readme is loaded.
 17type ReadmeMsg struct {
 18	Content string
 19	Path    string
 20}
 21
 22// Readme is the readme component page.
 23type Readme struct {
 24	common     common.Common
 25	code       *code.Code
 26	ref        RefMsg
 27	repo       proto.Repository
 28	readmePath string
 29	spinner    spinner.Model
 30	isLoading  bool
 31}
 32
 33// NewReadme creates a new readme model.
 34func NewReadme(common common.Common) *Readme {
 35	readme := code.New(common, "", "")
 36	readme.NoContentStyle = readme.NoContentStyle.SetString("No readme found.")
 37	readme.UseGlamour = true
 38	s := spinner.New(spinner.WithSpinner(spinner.Dot),
 39		spinner.WithStyle(common.Styles.Spinner))
 40	return &Readme{
 41		code:      readme,
 42		common:    common,
 43		spinner:   s,
 44		isLoading: true,
 45	}
 46}
 47
 48// Path implements common.TabComponent.
 49func (r *Readme) Path() string {
 50	return ""
 51}
 52
 53// TabName returns the name of the tab.
 54func (r *Readme) TabName() string {
 55	return "Readme"
 56}
 57
 58// SetSize implements common.Component.
 59func (r *Readme) SetSize(width, height int) {
 60	r.common.SetSize(width, height)
 61	r.code.SetSize(width, height)
 62}
 63
 64// ShortHelp implements help.KeyMap.
 65func (r *Readme) ShortHelp() []key.Binding {
 66	b := []key.Binding{
 67		r.common.KeyMap.UpDown,
 68	}
 69	return b
 70}
 71
 72// FullHelp implements help.KeyMap.
 73func (r *Readme) FullHelp() [][]key.Binding {
 74	k := r.code.KeyMap
 75	b := [][]key.Binding{
 76		{
 77			k.PageDown,
 78			k.PageUp,
 79			k.HalfPageDown,
 80			k.HalfPageUp,
 81		},
 82		{
 83			k.Down,
 84			k.Up,
 85			r.common.KeyMap.GotoTop,
 86			r.common.KeyMap.GotoBottom,
 87		},
 88	}
 89	return b
 90}
 91
 92// Init implements tea.Model.
 93func (r *Readme) Init() tea.Cmd {
 94	r.isLoading = true
 95	return tea.Batch(r.spinner.Tick, r.updateReadmeCmd)
 96}
 97
 98// Update implements tea.Model.
 99func (r *Readme) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
100	cmds := make([]tea.Cmd, 0)
101	switch msg := msg.(type) {
102	case RepoMsg:
103		r.repo = msg
104	case RefMsg:
105		r.ref = msg
106		cmds = append(cmds, r.Init())
107	case tea.WindowSizeMsg:
108		r.SetSize(msg.Width, msg.Height)
109	case EmptyRepoMsg:
110		cmds = append(cmds,
111			r.code.SetContent(defaultEmptyRepoMsg(r.common.Config(),
112				r.repo.Name()), ".md"),
113		)
114	case ReadmeMsg:
115		r.isLoading = false
116		r.readmePath = msg.Path
117		r.code.GotoTop()
118		cmds = append(cmds, r.code.SetContent(msg.Content, msg.Path))
119	case spinner.TickMsg:
120		if r.isLoading && r.spinner.ID() == msg.ID {
121			s, cmd := r.spinner.Update(msg)
122			r.spinner = s
123			if cmd != nil {
124				cmds = append(cmds, cmd)
125			}
126		}
127	}
128	c, cmd := r.code.Update(msg)
129	r.code = c.(*code.Code)
130	if cmd != nil {
131		cmds = append(cmds, cmd)
132	}
133	return r, tea.Batch(cmds...)
134}
135
136// View implements tea.Model.
137func (r *Readme) View() string {
138	if r.isLoading {
139		return renderLoading(r.common, r.spinner)
140	}
141	return r.code.View()
142}
143
144// SpinnerID implements common.TabComponent.
145func (r *Readme) SpinnerID() int {
146	return r.spinner.ID()
147}
148
149// StatusBarValue implements statusbar.StatusBar.
150func (r *Readme) StatusBarValue() string {
151	dir := filepath.Dir(r.readmePath)
152	if dir == "." || dir == "" {
153		return " "
154	}
155	return dir
156}
157
158// StatusBarInfo implements statusbar.StatusBar.
159func (r *Readme) StatusBarInfo() string {
160	return fmt.Sprintf("☰ %d%%", r.code.ScrollPosition())
161}
162
163func (r *Readme) updateReadmeCmd() tea.Msg {
164	m := ReadmeMsg{}
165	if r.repo == nil {
166		return common.ErrorMsg(common.ErrMissingRepo)
167	}
168	rm, rp, _ := backend.Readme(r.repo, r.ref)
169	m.Content = rm
170	m.Path = rp
171	return m
172}