1package tui
2
3import (
4 "fmt"
5 "smoothie/git"
6 "smoothie/tui/bubbles/commits"
7
8 tea "github.com/charmbracelet/bubbletea"
9 "github.com/gliderlabs/ssh"
10)
11
12type sessionState int
13
14const (
15 startState sessionState = iota
16 errorState
17 loadedState
18 quittingState
19 quitState
20)
21
22type stateMsg struct{ state sessionState }
23type infoMsg struct{ text string }
24type errMsg struct{ err error }
25
26func (e errMsg) Error() string {
27 return e.err.Error()
28}
29
30func SessionHandler(reposPath string) func(ssh.Session) (tea.Model, []tea.ProgramOption) {
31 rs := git.NewRepoSource(reposPath)
32 return func(s ssh.Session) (tea.Model, []tea.ProgramOption) {
33 if len(s.Command()) == 0 {
34 pty, changes, active := s.Pty()
35 if !active {
36 return nil, nil
37 }
38 return NewModel(pty.Window.Width, pty.Window.Height, changes, rs), []tea.ProgramOption{tea.WithAltScreen()}
39 }
40 return nil, nil
41 }
42}
43
44type Model struct {
45 state sessionState
46 error string
47 info string
48 width int
49 height int
50 windowChanges <-chan ssh.Window
51 repoSource *git.RepoSource
52 commitTimeline *commits.Bubble
53}
54
55func NewModel(width int, height int, windowChanges <-chan ssh.Window, repoSource *git.RepoSource) *Model {
56 m := &Model{
57 width: width,
58 height: height,
59 windowChanges: windowChanges,
60 repoSource: repoSource,
61 }
62 m.state = startState
63 return m
64}
65
66func (m *Model) Init() tea.Cmd {
67 return tea.Batch(m.windowChangesCmd, m.getCommitsCmd)
68}
69
70func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
71 cmds := make([]tea.Cmd, 0)
72 // Always allow state, error, info, window resize and quit messages
73 switch msg := msg.(type) {
74 case stateMsg:
75 m.state = msg.state
76 case tea.KeyMsg:
77 switch msg.String() {
78 case "q", "ctrl+c":
79 return m, tea.Quit
80 case "j", "k", "up", "down":
81 _, cmd := m.commitTimeline.Update(msg)
82 cmds = append(cmds, cmd)
83 }
84 case errMsg:
85 m.error = msg.Error()
86 m.state = errorState
87 return m, nil
88 case infoMsg:
89 m.info = msg.text
90 case windowMsg:
91 cmds = append(cmds, m.windowChangesCmd)
92 case tea.WindowSizeMsg:
93 m.width = msg.Width
94 m.height = msg.Height
95 m.commitTimeline.Height = msg.Height
96 }
97 return m, tea.Batch(cmds...)
98}
99
100func (m *Model) View() string {
101 pad := 6
102 h := headerStyle.Width(m.width - pad).Render("Charm Beta")
103 f := footerStyle.Render(m.info)
104 s := ""
105 content := ""
106 switch m.state {
107 case loadedState:
108 s += m.commitTimeline.View()
109 case errorState:
110 s += errorStyle.Render(fmt.Sprintf("Bummer: %s", m.error))
111 default:
112 s = normalStyle.Render(fmt.Sprintf("Doing something weird %d", m.state))
113 }
114 content = h + "\n" + s + "\n" + f
115 return appBoxStyle.Render(content)
116}