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