Skip to content

Commit

Permalink
Spinners, styling, tests (#36)
Browse files Browse the repository at this point in the history
* More 🧪

* Coloors

* Simpler & add a loading spinner

* Spin, spin, spin

* More cleanup

* Hide spinner

* escape filtering

* Fix tests
  • Loading branch information
adam7 committed May 19, 2023
1 parent c257212 commit dc62557
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 95 deletions.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

func main() {
models.MainModel = []tea.Model{&models.UserModel{}, &models.OrgModel{}}
models.MainModel = []tea.Model{models.NewUserModel(), &models.OrgModel{}}

p := tea.NewProgram(models.MainModel[models.UserModelName])
if _, err := p.Run(); err != nil {
Expand Down
36 changes: 25 additions & 11 deletions models/orgModel.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package models

import (
"fmt"
"log"

"hub-bub/keyMaps"
Expand All @@ -9,6 +10,7 @@ import (

"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/cli/go-gh"
Expand All @@ -24,10 +26,12 @@ type OrgModel struct {
help help.Model
keys keyMaps.OrgKeyMap

loaded bool
width int
height int
loaded bool
tabsHaveFocus bool
getting bool
spinner spinner.Model
}

func NewOrgModel(title string, width, height int) OrgModel {
Expand All @@ -39,6 +43,8 @@ func NewOrgModel(title string, width, height int) OrgModel {
keys: keyMaps.NewOrgKeyMap(),
repoModel: NewRepoModel(width/2, height),
repoList: list.New([]list.Item{}, list.NewDefaultDelegate(), width/2, height),
getting: true,
spinner: spinner.New(spinner.WithSpinner(spinner.Pulse)),
}
}

Expand All @@ -54,12 +60,8 @@ func (m *OrgModel) getSelectedRepo() structs.RepositoryQuery {
return m.RepoQuery.Organization.Repositories.Edges[m.repoList.Index()].Node
}

func (m *OrgModel) init(width, height int) {
m.loaded = true
}

func (m OrgModel) Init() tea.Cmd {
return nil
return m.spinner.Tick
}

func (m OrgModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
Expand All @@ -69,14 +71,15 @@ func (m OrgModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

case tea.WindowSizeMsg:
if !m.loaded {
m.init(msg.Width, msg.Height)
m.loaded = true
}
return m, nil

case messages.RepoListMsg:
m.repoList = buildRepoListModel(msg.OrganizationQuery, m.width, m.height)
m.RepoQuery = msg.OrganizationQuery
m.repoModel.SelectRepo(m.getSelectedRepo(), half(m.width), m.height)
m.getting = false
return m, nil

case tea.KeyMsg:
Expand All @@ -96,24 +99,35 @@ func (m OrgModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyEnter.String():
m.tabsHaveFocus = true
case tea.KeyEsc.String():
if !m.repoList.FilteringEnabled() {
if !m.repoList.SettingFilter() {
return MainModel[UserModelName], nil
}
m.repoList, cmd = m.repoList.Update(msg)
case "ctrl+c", "q":
if !m.repoList.FilteringEnabled() {
if !m.repoList.SettingFilter() {
return m, tea.Quit
}
case tea.KeyUp.String(), tea.KeyDown.String(), tea.KeyLeft.String(), tea.KeyRight.String():
case tea.KeyUp.String(), tea.KeyDown.String():
m.repoList, cmd = m.repoList.Update(msg)
m.repoModel.SelectRepo(m.getSelectedRepo(), half(m.width), m.height)
default:
m.repoList, cmd = m.repoList.Update(msg)
}
m.repoList, cmd = m.repoList.Update(msg)
}

default:
m.spinner, cmd = m.spinner.Update(msg)
return m, cmd
}

return m, cmd
}

func (m OrgModel) View() string {
if m.getting {
return fmt.Sprintf("%s getting repos ...", m.spinner.View())
}

var repoList = appStyle.Width(half(m.width)).Render(m.repoList.View())
var settings = appStyle.Width(half(m.width)).Render(m.repoModel.View())
help := m.helpView()
Expand Down
12 changes: 6 additions & 6 deletions models/repoModel.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,10 @@ func (m RepoModel) View() string {
}

settingsStyle := appStyle.Copy().Border(settingsBorder()).
BorderForeground(pink).Padding(0).Margin(0)
BorderForeground(blueLighter).Padding(0).Margin(0)

var tabs = RenderTabs(m.repoSettingsTabs, m.width, m.activeTab)
var settings = settingsStyle.Width(m.width - 2).Render(m.settingsTable.View())
// var settings = settingsStyle.Width(m.width - 2).Height(m.height - 7).Render(m.settingsTable.View())
var settings = settingsStyle.Width(m.width - 2).Height(m.height - 7).Render(m.settingsTable.View())

return lipgloss.JoinVertical(lipgloss.Left, tabs, settings)
}
Expand All @@ -111,8 +110,9 @@ func NewSettingsTable(activeSettings structs.RepositorySettingsTab, width int) t

func GetTableStyles() table.Styles {
return table.Styles{
Selected: lipgloss.NewStyle().Bold(true).Foreground(blueDarker),
Header: lipgloss.NewStyle().Bold(true).Foreground(pinkDarker).Underline(true).Padding(0, 0, 1, 0),
Cell: lipgloss.NewStyle().Padding(0),
Selected: lipgloss.NewStyle().Bold(true).Foreground(pink),
Header: lipgloss.NewStyle().Bold(true).Foreground(blue).BorderStyle(lipgloss.NormalBorder()).
BorderBottom(true).BorderForeground(blueLighter),
Cell: lipgloss.NewStyle().Padding(0),
}
}
26 changes: 13 additions & 13 deletions models/styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ import (
)

var (
// Colors
purple = lipgloss.Color("#cdb4db")
pink = lipgloss.Color("#ffc8dd")
pinkDarker = lipgloss.Color("#ffafcc")
blue = lipgloss.Color("#bde0fe")
blueDarker = lipgloss.Color("#a2d2ff")
// white = lipgloss.Color("#FFFDF5")
pink = lipgloss.Color("#f72585")
// purple = lipgloss.Color("#7209b7")
// purpleDarker = lipgloss.Color("#3a0ca3")
blue = lipgloss.Color("#4361ee")
blueLighter = lipgloss.Color("#4cc9f0")
white = lipgloss.Color("#dddddd")

appStyle = lipgloss.NewStyle().Padding(0, 0).Foreground(purple).BorderForeground(blue)
appStyle = lipgloss.NewStyle().Padding(0, 0).Foreground(white).BorderForeground(blue)

titleStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color(blueDarker)).
Foreground(blue).
BorderForeground(blueLighter).
Border(lipgloss.RoundedBorder(), true).
Padding(0, 1)

Expand All @@ -26,10 +26,10 @@ var (

func buildDefaultDelegate() list.DefaultDelegate {
defaultDelegate := list.NewDefaultDelegate()
defaultDelegate.Styles.SelectedTitle.Foreground(blueDarker)
defaultDelegate.Styles.SelectedTitle.BorderForeground(blueDarker)
defaultDelegate.Styles.SelectedDesc.Foreground(blueDarker)
defaultDelegate.Styles.SelectedDesc.BorderForeground(blueDarker)
defaultDelegate.Styles.SelectedTitle.Foreground(pink)
defaultDelegate.Styles.SelectedTitle.BorderForeground(pink)
defaultDelegate.Styles.SelectedDesc.Foreground(pink).Faint(true)
defaultDelegate.Styles.SelectedDesc.BorderForeground(pink)

return defaultDelegate
}
8 changes: 3 additions & 5 deletions models/tabs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func RenderTabs(tabSettings []structs.RepositorySettingsTab, width, activeTab int) string {
inactiveTabBorder := tabBorderWithBottom("┴", "─", "┴")
activeTabBorder := tabBorderWithBottom("┘", " ", "└")
inactiveTabStyle := lipgloss.NewStyle().Border(inactiveTabBorder, true).BorderForeground(pink).Padding(0)
inactiveTabStyle := lipgloss.NewStyle().Border(inactiveTabBorder, true).BorderForeground(blueLighter).Padding(0)
activeTabStyle := inactiveTabStyle.Copy().Border(activeTabBorder, true)

tabs := []string{}
Expand Down Expand Up @@ -40,12 +40,10 @@ func RenderTabs(tabSettings []structs.RepositorySettingsTab, width, activeTab in
border.BottomRight = "┤"
}

style = style.Border(border)
style = style.Border(border).Align(lipgloss.Center).MaxHeight(3)

if i == activeTab {
style = style.Foreground(blueDarker)
} else {
style = style.Foreground(pinkDarker)
style = style.Foreground(pink)
}

if isLast {
Expand Down
6 changes: 3 additions & 3 deletions models/tabs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,23 @@ func TestTabs_RenderTabs(t *testing.T) {
width: 35,
activeTab: 0,
},
want: "╭─────────╮╭─────────╮╭───────────╮\n│Tab 1 ││Tab 2 ││Tab 3 \n│ └┴─────────┴┴───────────┤"},
want: "╭─────────╮╭─────────╮╭───────────╮\n Tab 1 ││ Tab 2 ││ Tab 3\n│ └┴─────────┴┴───────────┤"},
{
name: "Three Tabs, Active 1",
args: args{
tabSettings: threeTabs,
width: 30,
activeTab: 1,
},
want: "╭────────╮╭────────╮╭────────╮\n│Tab 1 ││Tab 2 ││Tab 3 \n├────────┴┘ └┴────────┤"},
want: "╭────────╮╭────────╮╭────────╮\n Tab 1 ││ Tab 2 ││ Tab 3 │\n├────────┴┘ └┴────────┤"},
{
name: "Three Tabs, Active 2",
args: args{
tabSettings: threeTabs,
width: 25,
activeTab: 2,
},
want: "╭──────╮╭──────╮╭───────╮\n│Tab 1 ││Tab 2 ││Tab 3 \n├──────┴┴──────┴┘ │"},
want: "╭──────╮╭──────╮╭───────╮\n│Tab 1 ││Tab 2 ││ Tab 3 │\n├──────┴┴──────┴┘ │"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
37 changes: 27 additions & 10 deletions models/userModel.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,37 @@ import (
"github.com/cli/go-gh"

"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
)

type UserModel struct {
Authenticating bool
Authenticated bool
User structs.User
SelectedOrgUrl string
list list.Model
loaded bool
width int
height int
spinner spinner.Model
}

func (m *UserModel) initList() {
m.list = list.New(
[]list.Item{},
list.NewDefaultDelegate(),
m.width,
m.height,
)
func NewUserModel() UserModel {
return UserModel{
Authenticating: true,
list: list.New(
[]list.Item{},
list.NewDefaultDelegate(),
0,
0,
),
spinner: spinner.New(spinner.WithSpinner(spinner.Pulse)),
}
}

func (m UserModel) Init() tea.Cmd {
return checkLoginStatus
return tea.Batch(checkLoginStatus, m.spinner.Tick)
}

func (m UserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
Expand All @@ -45,17 +52,20 @@ func (m UserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.width = msg.Width

if !m.loaded {
m.initList()
m.list.SetWidth(m.width)
m.list.SetHeight(m.height)
m.loaded = true
}
return m, nil

case messages.AuthenticationMsg:
m.Authenticating = false
m.Authenticated = true
m.User = msg.User
return m, getOrganisations

case messages.AuthenticationErrorMsg:
m.Authenticating = false
m.Authenticated = false
return m, nil

Expand All @@ -76,6 +86,10 @@ func (m UserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

return orgModel, orgModel.GetRepositories
}

default:
m.spinner, cmd = m.spinner.Update(msg)
return m, cmd
}

m.list, cmd = m.list.Update(msg)
Expand All @@ -84,9 +98,12 @@ func (m UserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

func (m UserModel) View() string {
if !m.Authenticated {
if !m.Authenticating && !m.Authenticated {
return fmt.Sprintln("You are not authenticated try running `gh auth login`. Press q to quit.")
}
if m.Authenticating {
return fmt.Sprintf("%s Authenticating with github", m.spinner.View())
}

return appStyle.Render(m.list.View())
}
Expand Down
Loading

0 comments on commit dc62557

Please sign in to comment.