diff --git a/internal/terminal/handler/handler.go b/internal/terminal/handler/handler.go index c4333dc..dcd2167 100644 --- a/internal/terminal/handler/handler.go +++ b/internal/terminal/handler/handler.go @@ -2,12 +2,7 @@ package handler import ( "fmt" - "github.com/termkit/gama/internal/terminal/handler/header" - "strings" - "time" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/timer" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" @@ -16,10 +11,12 @@ import ( hdltrigger "github.com/termkit/gama/internal/terminal/handler/ghtrigger" hdlWorkflow "github.com/termkit/gama/internal/terminal/handler/ghworkflow" hdlworkflowhistory "github.com/termkit/gama/internal/terminal/handler/ghworkflowhistory" + "github.com/termkit/gama/internal/terminal/handler/header" hdlinfo "github.com/termkit/gama/internal/terminal/handler/information" hdltypes "github.com/termkit/gama/internal/terminal/handler/types" ts "github.com/termkit/gama/internal/terminal/style" pkgversion "github.com/termkit/gama/pkg/version" + "strings" ) type model struct { @@ -28,7 +25,6 @@ type model struct { // models viewport *viewport.Model - timer timer.Model modelHeader *header.Header modelInfo *hdlinfo.ModelInfo @@ -68,7 +64,6 @@ func SetupTerminal(githubUseCase gu.UseCase, version pkgversion.Version) tea.Mod m := model{ viewport: vp, - timer: timer.NewWithInterval(1<<63-1, time.Millisecond*200), modelHeader: hdlModelHeader, modelInfo: hdlModelInfo, SelectedRepository: selectedRepository, @@ -86,7 +81,7 @@ func (m *model) Init() tea.Cmd { return tea.Batch( tea.EnterAltScreen, tea.SetWindowTitle("GitHub Actions Manager (GAMA)"), - m.timer.Init(), + m.modelHeader.Init(), m.modelInfo.Init(), m.modelGithubRepository.Init(), m.modelWorkflowHistory.Init(), @@ -103,17 +98,22 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.viewport.Width = msg.Width m.viewport.Height = msg.Height case tea.KeyMsg: - m.modelHeader, cmd = m.modelHeader.Update(msg) - cmds = append(cmds, cmd) - cmds = append(cmds, m.handleTabContent(cmd, msg)) - + // Handle global keybindings switch { case key.Matches(msg, m.keys.Quit): return m, tea.Quit } - case timer.TickMsg: - m.timer, cmd = m.timer.Update(msg) + + m.modelHeader, cmd = m.modelHeader.Update(msg) + cmds = append(cmds, cmd) + case hdlinfo.UpdateSpinnerMsg: + m.modelInfo, cmd = m.modelInfo.Update(msg) + cmds = append(cmds, cmd) + case header.UpdateMsg: + m.modelHeader, cmd = m.modelHeader.Update(msg) cmds = append(cmds, cmd) + default: + cmds = append(cmds, m.handleTabContent(cmd, msg)) } return m, tea.Batch(cmds...) diff --git a/internal/terminal/handler/header/header.go b/internal/terminal/handler/header/header.go index 2cd294e..1f9ca8a 100644 --- a/internal/terminal/handler/header/header.go +++ b/internal/terminal/handler/header/header.go @@ -5,8 +5,10 @@ import ( "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + ts "github.com/termkit/gama/internal/terminal/style" "strings" "sync" + "time" ) // Header is a helper for rendering the Header of the terminal. @@ -14,7 +16,8 @@ type Header struct { keys keyMap Viewport *viewport.Model - //timer timer.Model + + tickerInterval time.Duration currentTab int lockTabs bool @@ -51,10 +54,11 @@ var ( func NewHeader(viewport *viewport.Model) *Header { once.Do(func() { h = &Header{ - Viewport: viewport, - currentTab: 0, - lockTabs: true, - keys: keys, + tickerInterval: time.Millisecond * 250, + Viewport: viewport, + currentTab: 0, + lockTabs: true, + keys: keys, } }) return h @@ -94,9 +98,36 @@ func (h *Header) SetSpecialHeader(header string, firstStyle, secondStyle lipglos }) } +type UpdateMsg struct { + Msg string + UpdatingComponent string +} + func (h *Header) Init() tea.Cmd { - //return h.timer.Init() - return nil + return h.tick() +} + +func (h *Header) tick() tea.Cmd { + t := time.NewTimer(h.tickerInterval) + return func() tea.Msg { + //ts := <-t.C + //t.Stop() + //for len(t.C) > 0 { + // <-t.C + //} + //return UpdateMsg{ + // Msg: "tick", + // UpdatingComponent: "header", + //} + + select { + case <-t.C: + return UpdateMsg{ + Msg: "tick", + UpdatingComponent: "header", + } + } + } } func (h *Header) Update(msg tea.Msg) (*Header, tea.Cmd) { @@ -114,29 +145,10 @@ func (h *Header) Update(msg tea.Msg) (*Header, tea.Cmd) { h.currentTab = min(h.currentTab+1, len(h.commonHeaders)-1) } } + case UpdateMsg: + h.switchSpecialAnimation = !h.switchSpecialAnimation - //case timer.TickMsg: - //h.switchSpecialAnimation = !h.switchSpecialAnimation - - //var cmd tea.Cmd - //h.timer, cmd = h.timer.Update(msg) - //return h, cmd - // - //case timer.StartStopMsg: - // var cmd tea.Cmd - //h.timer, cmd = h.timer.Update(msg) - //return h, cmd - - //case tea.KeyMsg: - // switch { - // case key.Matches(msg, m.keymap.quit): - // m.quitting = true - // return m, tea.Quit - // case key.Matches(msg, m.keymap.reset): - // m.timer.Timeout = timeout - // case key.Matches(msg, m.keymap.start, m.keymap.stop): - // return m, m.timer.Toggle() - // } + return h, h.Init() } return h, nil @@ -158,14 +170,22 @@ func (h *Header) View() string { var renderedTitles []string for i, title := range h.commonHeaders { - if i == h.currentTab { - renderedTitles = append(renderedTitles, title.activeStyle.Render(title.header)) + if h.lockTabs { + if i == 0 { + renderedTitles = append(renderedTitles, title.activeStyle.Render(title.header)) + } else { + renderedTitles = append(renderedTitles, ts.TitleStyleDisabled.Render(title.header)) + } } else { - renderedTitles = append(renderedTitles, title.inactiveStyle.Render(title.header)) + if i == h.currentTab { + renderedTitles = append(renderedTitles, title.activeStyle.Render(title.header)) + } else { + renderedTitles = append(renderedTitles, title.inactiveStyle.Render(title.header)) + } } } - if h.currentTab == -1 { + if h.switchSpecialAnimation { specialHeader = h.specialHeaders[0].firstStyle.Render(h.specialHeaders[0].header) } else { specialHeader = h.specialHeaders[0].secondStyle.Render(h.specialHeaders[0].header) diff --git a/internal/terminal/handler/information/information.go b/internal/terminal/handler/information/information.go index 952b688..56af815 100644 --- a/internal/terminal/handler/information/information.go +++ b/internal/terminal/handler/information/information.go @@ -3,19 +3,17 @@ package information import ( "context" "fmt" - "github.com/termkit/gama/internal/terminal/handler/header" - "strings" - "time" - "github.com/charmbracelet/bubbles/help" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" gu "github.com/termkit/gama/internal/github/usecase" hdlerror "github.com/termkit/gama/internal/terminal/handler/error" + "github.com/termkit/gama/internal/terminal/handler/header" pkgversion "github.com/termkit/gama/pkg/version" + "strings" + "time" ) type ModelInfo struct { @@ -24,6 +22,8 @@ type ModelInfo struct { // use cases github gu.UseCase + complete bool + // models modelHeader *header.Header @@ -37,6 +37,8 @@ type ModelInfo struct { } const ( + spinnerInterval = 100 * time.Millisecond + releaseURL = "https://github.com/termkit/gama/releases" applicationName = ` @@ -59,7 +61,7 @@ func SetupModelInfo(viewport *viewport.Model, githubUseCase gu.UseCase, version hdlModelHeader := header.NewHeader(viewport) s := spinner.New() - s.Spinner = spinner.Pulse + s.Spinner = spinner.Dot s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("120")) return &ModelInfo{ @@ -74,13 +76,25 @@ func SetupModelInfo(viewport *viewport.Model, githubUseCase gu.UseCase, version } } +type UpdateSpinnerMsg string + func (m *ModelInfo) Init() tea.Cmd { currentVersion = m.version.CurrentVersion() applicationDescription = fmt.Sprintf("Github Actions Manager (%s)", currentVersion) go m.testConnection(context.Background()) go m.checkUpdates(context.Background()) - return nil + return m.tickSpinner() +} + +func (m *ModelInfo) tickSpinner() tea.Cmd { + t := time.NewTimer(spinnerInterval) + return func() tea.Msg { + select { + case <-t.C: + return UpdateSpinnerMsg("tick") + } + } } func (m *ModelInfo) checkUpdates(ctx context.Context) { @@ -101,14 +115,15 @@ func (m *ModelInfo) checkUpdates(ctx context.Context) { func (m *ModelInfo) Update(msg tea.Msg) (*ModelInfo, tea.Cmd) { var cmd tea.Cmd - switch msg := msg.(type) { - case tea.WindowSizeMsg: - m.Help.Width = msg.Width - case tea.KeyMsg: - switch { - case key.Matches(msg, m.Keys.Quit): - return m, tea.Quit + switch msg.(type) { + case UpdateSpinnerMsg: + if m.complete { + return m, nil } + + m.modelError.SetProgressMessage("Checking your token " + m.spinner.View()) + m.spinner, cmd = m.spinner.Update(m.spinner.Tick()) + return m, m.tickSpinner() } return m, cmd @@ -134,23 +149,7 @@ func (m *ModelInfo) View() string { } func (m *ModelInfo) testConnection(ctx context.Context) { - ctxWithCancel, cancel := context.WithCancel(ctx) - - // TODO: make it better - go func(ctx context.Context) { - for { - select { - case <-ctx.Done(): - return - default: - m.spinner, _ = m.spinner.Update(m.spinner.Tick()) - m.modelError.SetProgressMessage("Checking your token " + m.spinner.View()) - time.Sleep(200 * time.Millisecond) - } - } - }(ctxWithCancel) - defer cancel() - + time.Sleep(time.Second * 5) // TODO : remove, just for testing spinner _, err := m.github.GetAuthUser(ctx) if err != nil { m.modelError.SetError(err) @@ -162,8 +161,7 @@ func (m *ModelInfo) testConnection(ctx context.Context) { m.modelError.Reset() m.modelError.SetSuccessMessage("Welcome to GAMA!") m.modelHeader.SetLockTabs(false) - - go m.Update(m) + m.complete = true } func (m *ModelInfo) ViewStatus() string {