readme.go

  1package repo
  2
  3import (
  4	"fmt"
  5	"path/filepath"
  6
  7	"github.com/charmbracelet/bubbles/key"
  8	"github.com/charmbracelet/bubbles/spinner"
  9	tea "github.com/charmbracelet/bubbletea"
 10	"github.com/charmbracelet/soft-serve/server/backend"
 11	"github.com/charmbracelet/soft-serve/server/proto"
 12	"github.com/charmbracelet/soft-serve/server/ui/common"
 13	"github.com/charmbracelet/soft-serve/server/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.Copy().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// TabName returns the name of the tab.
 49func (r *Readme) TabName() string {
 50	return "Readme"
 51}
 52
 53// SetSize implements common.Component.
 54func (r *Readme) SetSize(width, height int) {
 55	r.common.SetSize(width, height)
 56	r.code.SetSize(width, height)
 57}
 58
 59// ShortHelp implements help.KeyMap.
 60func (r *Readme) ShortHelp() []key.Binding {
 61	b := []key.Binding{
 62		r.common.KeyMap.UpDown,
 63	}
 64	return b
 65}
 66
 67// FullHelp implements help.KeyMap.
 68func (r *Readme) FullHelp() [][]key.Binding {
 69	k := r.code.KeyMap
 70	b := [][]key.Binding{
 71		{
 72			k.PageDown,
 73			k.PageUp,
 74			k.HalfPageDown,
 75			k.HalfPageUp,
 76		},
 77		{
 78			k.Down,
 79			k.Up,
 80			r.common.KeyMap.GotoTop,
 81			r.common.KeyMap.GotoBottom,
 82		},
 83	}
 84	return b
 85}
 86
 87// Init implements tea.Model.
 88func (r *Readme) Init() tea.Cmd {
 89	r.isLoading = true
 90	return tea.Batch(r.spinner.Tick, r.updateReadmeCmd)
 91}
 92
 93// Update implements tea.Model.
 94func (r *Readme) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 95	cmds := make([]tea.Cmd, 0)
 96	switch msg := msg.(type) {
 97	case RepoMsg:
 98		r.repo = msg
 99	case RefMsg:
100		r.ref = msg
101		cmds = append(cmds, r.Init())
102	case tea.WindowSizeMsg:
103		r.SetSize(msg.Width, msg.Height)
104	case EmptyRepoMsg:
105		cmds = append(cmds,
106			r.code.SetContent(defaultEmptyRepoMsg(r.common.Config(),
107				r.repo.Name()), ".md"),
108		)
109	case ReadmeMsg:
110		r.isLoading = false
111		r.readmePath = msg.Path
112		r.code.GotoTop()
113		cmds = append(cmds, r.code.SetContent(msg.Content, msg.Path))
114	case spinner.TickMsg:
115		if r.isLoading && r.spinner.ID() == msg.ID {
116			s, cmd := r.spinner.Update(msg)
117			r.spinner = s
118			if cmd != nil {
119				cmds = append(cmds, cmd)
120			}
121		}
122	}
123	c, cmd := r.code.Update(msg)
124	r.code = c.(*code.Code)
125	if cmd != nil {
126		cmds = append(cmds, cmd)
127	}
128	return r, tea.Batch(cmds...)
129}
130
131// View implements tea.Model.
132func (r *Readme) View() string {
133	if r.isLoading {
134		return renderLoading(r.common, r.spinner)
135	}
136	return r.code.View()
137}
138
139// SpinnerID implements common.TabComponent.
140func (r *Readme) SpinnerID() int {
141	return r.spinner.ID()
142}
143
144// StatusBarValue implements statusbar.StatusBar.
145func (r *Readme) StatusBarValue() string {
146	dir := filepath.Dir(r.readmePath)
147	if dir == "." || dir == "" {
148		return " "
149	}
150	return dir
151}
152
153// StatusBarInfo implements statusbar.StatusBar.
154func (r *Readme) StatusBarInfo() string {
155	return fmt.Sprintf("☰ %d%%", r.code.ScrollPosition())
156}
157
158func (r *Readme) updateReadmeCmd() tea.Msg {
159	m := ReadmeMsg{}
160	if r.repo == nil {
161		return common.ErrorMsg(common.ErrMissingRepo)
162	}
163	rm, rp, _ := backend.Readme(r.repo, r.ref)
164	m.Content = rm
165	m.Path = rp
166	return m
167}