1package selection
2
3import (
4 "fmt"
5 "time"
6
7 "github.com/charmbracelet/bubbles/key"
8 "github.com/charmbracelet/bubbles/list"
9 tea "github.com/charmbracelet/bubbletea"
10 "github.com/charmbracelet/lipgloss"
11 appCfg "github.com/charmbracelet/soft-serve/config"
12 "github.com/charmbracelet/soft-serve/ui/common"
13 "github.com/charmbracelet/soft-serve/ui/components/code"
14 "github.com/charmbracelet/soft-serve/ui/components/selector"
15 "github.com/charmbracelet/soft-serve/ui/components/yankable"
16 "github.com/charmbracelet/soft-serve/ui/session"
17)
18
19type Selection struct {
20 s session.Session
21 common common.Common
22 readme *code.Code
23 selector *selector.Selector
24}
25
26func New(s session.Session, common common.Common) *Selection {
27 sel := &Selection{
28 s: s,
29 common: common,
30 readme: code.New(common, "", ""),
31 selector: selector.New(common, []list.Item{}),
32 }
33 return sel
34}
35
36func (s *Selection) SetSize(width, height int) {
37 s.common.SetSize(width, height)
38 s.readme.SetSize(width, height)
39 s.selector.SetSize(width, height)
40}
41
42func (s *Selection) ShortHelp() []key.Binding {
43 k := s.selector.KeyMap()
44 return []key.Binding{
45 s.common.Keymap.UpDown,
46 s.common.Keymap.Select,
47 k.Filter,
48 k.ClearFilter,
49 }
50}
51
52func (s *Selection) FullHelp() [][]key.Binding {
53 k := s.selector.KeyMap()
54 return [][]key.Binding{
55 {
56 k.CursorUp,
57 k.CursorDown,
58 k.NextPage,
59 k.PrevPage,
60 k.GoToStart,
61 k.GoToEnd,
62 },
63 {
64 k.Filter,
65 k.ClearFilter,
66 k.CancelWhileFiltering,
67 k.AcceptWhileFiltering,
68 k.ShowFullHelp,
69 k.CloseFullHelp,
70 },
71 // Ignore the following keys:
72 // k.Quit,
73 // k.ForceQuit,
74 }
75}
76
77func (s *Selection) Init() tea.Cmd {
78 items := make([]list.Item, 0)
79 cfg := s.s.Config()
80 yank := func(text string) *yankable.Yankable {
81 return yankable.New(
82 lipgloss.NewStyle().Foreground(lipgloss.Color("168")),
83 lipgloss.NewStyle().Foreground(lipgloss.Color("168")).SetString("Copied!"),
84 text,
85 )
86 }
87 // Put configured repos first
88 for _, r := range cfg.Repos {
89 items = append(items, selector.Item{
90 Title: r.Name,
91 Name: r.Repo,
92 Description: r.Note,
93 LastUpdate: time.Now(),
94 URL: yank(repoUrl(cfg, r.Name)),
95 })
96 }
97 for _, r := range cfg.Source.AllRepos() {
98 exists := false
99 for _, item := range items {
100 item := item.(selector.Item)
101 if item.Name == r.Name() {
102 exists = true
103 break
104 }
105 }
106 if !exists {
107 items = append(items, selector.Item{
108 Title: r.Name(),
109 Name: r.Name(),
110 Description: "",
111 LastUpdate: time.Now(),
112 URL: yank(repoUrl(cfg, r.Name())),
113 })
114 }
115 }
116 return tea.Batch(
117 s.selector.Init(),
118 s.selector.SetItems(items),
119 )
120}
121
122func (s *Selection) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
123 cmds := make([]tea.Cmd, 0)
124 switch msg := msg.(type) {
125 case selector.ActiveMsg:
126 cmds = append(cmds, s.changeActive(msg))
127 default:
128 m, cmd := s.selector.Update(msg)
129 s.selector = m.(*selector.Selector)
130 if cmd != nil {
131 cmds = append(cmds, cmd)
132 }
133 }
134 return s, tea.Batch(cmds...)
135}
136
137func (s *Selection) View() string {
138 return lipgloss.JoinHorizontal(
139 lipgloss.Top,
140 s.readme.View(),
141 s.selector.View(),
142 )
143}
144
145func (s *Selection) changeActive(msg selector.ActiveMsg) tea.Cmd {
146 cfg := s.s.Config()
147 r, err := cfg.Source.GetRepo(string(msg))
148 if err != nil {
149 return common.ErrorCmd(err)
150 }
151 rm, rp := r.Readme()
152 return s.readme.SetContent(rm, rp)
153}
154
155func repoUrl(cfg *appCfg.Config, name string) string {
156 port := ""
157 if cfg.Port != 22 {
158 port += fmt.Sprintf(":%d", cfg.Port)
159 }
160 return fmt.Sprintf("git clone ssh://%s/%s", cfg.Host+port, name)
161}