diff --git a/README.md b/README.md index 3a3e717..9cec453 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,41 @@ docker run --rm -it victorbersy/docker-hub-cli docker pull ghcr.io/victorbersy/docker-hub-cli:latest docker run --rm -it ghcr.io/victorbersy/docker-hub-cli ``` + +## Authentication + +The authentication system is rough at the moment. Hopefully, it will be better soon. Meanwhile, here's how to authenticate: + +Set `DOCKER_BEARER` with your token and launch the program: + +```console +$ DOCKER_BEARER=$DOCKER_BEARER DOCKER_USERNAME=victorbersy go run main.go +``` + +or with a compiled version: +```console +$ DOCKER_BEARER=$DOCKER_BEARER DOCKER_USERNAME=victorbersy ./docker-hub-cli +``` + +To get your Bearer token: +```console +$ username=victorbersy \ + password=my_docker_pat_token \ + curl -s 'https://hub.docker.com/api/v2/users/login' \ + -H 'Accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{"username":"'$username'","password":"'$password'"}' | jq -r .token +``` + +To generate your Personal Access Token (PAT): [hub.docker.com/settings/security](https://hub.docker.com/settings/security) + ## Screenshots +### Dark default theme ![image](https://user-images.githubusercontent.com/2109178/180597089-22be7878-8a27-4fe6-8401-be28cc26fa0b.png) ![image](https://user-images.githubusercontent.com/2109178/180597084-c1e26447-91ce-4b82-994f-481886776fad.png) + +### Light default theme ![image](https://user-images.githubusercontent.com/2109178/180597083-66beebdb-3b60-401f-ab78-343b866b3986.png) ![image](https://user-images.githubusercontent.com/2109178/180597082-180ff55a-18e1-4056-83a9-26694c1fcc23.png) diff --git a/internal/data/repositories.go b/internal/data/search/repositories.go similarity index 84% rename from internal/data/repositories.go rename to internal/data/search/repositories.go index 8b270bb..de6d21e 100644 --- a/internal/data/repositories.go +++ b/internal/data/search/repositories.go @@ -1,4 +1,4 @@ -package data +package data_search import ( "fmt" @@ -11,11 +11,11 @@ import ( ) type RepositoryPage struct { - PageSize int `json:"page_size"` - PageId int `json:"page"` - NextUrl string `json:"next"` - PreviousUrl string `json:"previous"` - Repositories []RepositoryData `json:"summaries"` + PageSize int `json:"page_size"` + PageId int `json:"page"` + NextUrl string `json:"next"` + PreviousUrl string `json:"previous"` + Repositories []Repository `json:"summaries"` } type Category struct { @@ -28,7 +28,7 @@ type OperatingSystem struct { Label string `json:"label"` } -type RepositoryData struct { +type Repository struct { Architectures []Architecture `json:"architectures"` Categories []Category `json:"categories"` CertificationStatus string `json:"certification_status"` @@ -43,7 +43,7 @@ type RepositoryData struct { Source string `json:"source"` StarCount int `json:"star_count"` Type string `json:"type"` - Updated_at time.Time `json:"updated_at"` + UpdatedAt time.Time `json:"updated_at"` Labels []Label } @@ -64,22 +64,14 @@ type Architecture struct { Label string `json:"label"` } -func (data RepositoryData) GetRepoNameWithOwner() string { - return data.Name -} - -func (data RepositoryData) GetUrl() string { +func (data Repository) GetUrl() string { if data.Publisher.Id == "docker" { return fmt.Sprintf("https://hub.docker.com/_/%s", data.Slug) } return fmt.Sprintf("https://hub.docker.com/r/%s", data.Slug) } -func (data RepositoryData) GetLastUpdate() time.Time { - return data.Updated_at -} - -func (data *RepositoryData) setLabels() { +func (data *Repository) setLabels() { data.Labels = append(data.Labels, Label{ Name: "Docker Official", Glyph: constants.GlyphLabelDockerOfficial, @@ -106,7 +98,7 @@ func (data *RepositoryData) setLabels() { }) } -func FetchRepositories() ([]RepositoryData, error) { +func FetchRepositories() ([]Repository, error) { client := req.C(). SetTimeout(5 * time.Second) diff --git a/internal/data/user/repositories.go b/internal/data/user/repositories.go new file mode 100644 index 0000000..5bef358 --- /dev/null +++ b/internal/data/user/repositories.go @@ -0,0 +1,69 @@ +package data_user + +import ( + "fmt" + "log" + "os" + "time" + + "github.com/imroc/req/v3" +) + +type RepositoryPage struct { + Count int `json:"count"` + NextUrl string `json:"next"` + PreviousUrl string `json:"previous"` + Repositories []Repository `json:"results"` +} + +type Repository struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + RepositoryType string `json:"repository_type"` + Status int `json:"status"` + IsPrivate bool `json:"is_private"` + StarCount int `json:"star_count"` + PullCount int `json:"pull_count"` + UpdatedAt time.Time `json:"last_updated"` + CreatedAt time.Time `json:"date_registered"` + Affiliation string `json:"affiliation"` +} + +func (data Repository) GetUrl() string { + return fmt.Sprintf("https://hub.docker.com/repository/docker/%s/%s", data.Namespace, data.Name) +} + +func FetchRepositories() ([]Repository, error) { + client := req.C(). + SetTimeout(5 * time.Second) + + var repositoryPage RepositoryPage + if os.Getenv("DOCKER_USERNAME") == "" { + return nil, nil + } + repo_url := fmt.Sprintf("https://hub.docker.com/v2/repositories/%s", os.Getenv("DOCKER_USERNAME")) + resp, err := client.R(). + SetHeader("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("DOCKER_BEARER"))). + SetResult(&repositoryPage). + EnableDump(). + SetQueryParam("page_size", "100"). + SetQueryParam("page", "1"). + SetQueryParam("ordering", "last_updated"). + Get(repo_url) + if err != nil { + log.Println("error:", err) + log.Println("raw content:") + log.Println(resp.Dump()) + return nil, err + } + + if resp.IsSuccess() { + return repositoryPage.Repositories, err + } + + log.Println("unknown status", resp.Status) + log.Println("raw content:") + log.Println(resp.Dump()) // Record raw content when server returned unknown status code. + + return repositoryPage.Repositories, err +} diff --git a/internal/data/utils.go b/internal/data/utils.go index 7d0bfa6..7f71b8c 100644 --- a/internal/data/utils.go +++ b/internal/data/utils.go @@ -1,9 +1,5 @@ package data -import "time" - type RowData interface { - GetRepoNameWithOwner() string GetUrl() string - GetLastUpdate() time.Time } diff --git a/internal/ui/components/repository/repository.go b/internal/ui/components/repository/repository.go index 4f33bb5..50a4378 100644 --- a/internal/ui/components/repository/repository.go +++ b/internal/ui/components/repository/repository.go @@ -1,70 +1 @@ package repository - -import ( - "fmt" - - "github.com/charmbracelet/lipgloss" - "github.com/victorbersy/docker-hub-cli/internal/data" - "github.com/victorbersy/docker-hub-cli/internal/ui/components/table" - "github.com/victorbersy/docker-hub-cli/internal/utils" -) - -type Repository struct { - Data data.RepositoryData -} - -func (repo Repository) ToTableRow() table.Row { - return table.Row{ - repo.renderName(), - repo.renderLabels(), - repo.renderPublisher(), - repo.renderstatsDownloads(), - repo.renderstatsStars(), - repo.renderLastUpdate(), - repo.renderDescription(), - } -} - -func (repo Repository) renderName() string { - return repo.Data.Name -} - -func (repo Repository) renderLabels() string { - labels := []string{} - for _, label := range repo.Data.Labels { - if label.Enabled { - labels = append(labels, lipgloss.NewStyle().Foreground(label.Color).Width(3).Render(label.Glyph)) - } else { - labels = append(labels, lipgloss.NewStyle().Width(3).Render("")) - } - } - return lipgloss.JoinHorizontal( - lipgloss.Top, - labels..., - ) -} - -func (repo Repository) renderPublisher() string { - return lipgloss.NewStyle(). - Render(repo.Data.Publisher.Name) -} - -func (repo Repository) renderstatsDownloads() string { - return lipgloss.NewStyle(). - Render(repo.Data.PullCount) -} - -func (repo Repository) renderstatsStars() string { - return lipgloss.NewStyle(). - Render(fmt.Sprint(repo.Data.StarCount)) -} - -func (repo Repository) renderDescription() string { - return lipgloss.NewStyle(). - Render(repo.Data.Description) -} - -func (repo Repository) renderLastUpdate() string { - return lipgloss.NewStyle(). - Render(utils.TimeElapsed(repo.Data.Updated_at)) -} diff --git a/internal/ui/components/repository/search/repository.go b/internal/ui/components/repository/search/repository.go new file mode 100644 index 0000000..c9fdd0b --- /dev/null +++ b/internal/ui/components/repository/search/repository.go @@ -0,0 +1,70 @@ +package repository_search + +import ( + "fmt" + + "github.com/charmbracelet/lipgloss" + data_search "github.com/victorbersy/docker-hub-cli/internal/data/search" + "github.com/victorbersy/docker-hub-cli/internal/ui/components/table" + "github.com/victorbersy/docker-hub-cli/internal/utils" +) + +type Repository struct { + Data data_search.Repository +} + +func (repo Repository) ToTableRow() table.Row { + return table.Row{ + repo.renderName(), + repo.renderLabels(), + repo.renderPublisher(), + repo.renderstatsDownloads(), + repo.renderstatsStars(), + repo.renderLastUpdate(), + repo.renderDescription(), + } +} + +func (repo Repository) renderName() string { + return repo.Data.Name +} + +func (repo Repository) renderLabels() string { + labels := []string{} + for _, label := range repo.Data.Labels { + if label.Enabled { + labels = append(labels, lipgloss.NewStyle().Foreground(label.Color).Width(3).Render(label.Glyph)) + } else { + labels = append(labels, lipgloss.NewStyle().Width(3).Render("")) + } + } + return lipgloss.JoinHorizontal( + lipgloss.Top, + labels..., + ) +} + +func (repo Repository) renderPublisher() string { + return lipgloss.NewStyle(). + Render(repo.Data.Publisher.Name) +} + +func (repo Repository) renderstatsDownloads() string { + return lipgloss.NewStyle(). + Render(repo.Data.PullCount) +} + +func (repo Repository) renderstatsStars() string { + return lipgloss.NewStyle(). + Render(fmt.Sprint(repo.Data.StarCount)) +} + +func (repo Repository) renderDescription() string { + return lipgloss.NewStyle(). + Render(repo.Data.Description) +} + +func (repo Repository) renderLastUpdate() string { + return lipgloss.NewStyle(). + Render(utils.TimeElapsed(repo.Data.UpdatedAt)) +} diff --git a/internal/ui/components/repository/user/repository.go b/internal/ui/components/repository/user/repository.go new file mode 100644 index 0000000..369dd7b --- /dev/null +++ b/internal/ui/components/repository/user/repository.go @@ -0,0 +1,58 @@ +package repository_user + +import ( + "fmt" + + "github.com/charmbracelet/lipgloss" + data_user "github.com/victorbersy/docker-hub-cli/internal/data/user" + "github.com/victorbersy/docker-hub-cli/internal/ui/components/table" + "github.com/victorbersy/docker-hub-cli/internal/ui/constants" + "github.com/victorbersy/docker-hub-cli/internal/utils" +) + +type Repository struct { + Data data_user.Repository +} + +func (repo Repository) ToTableRow() table.Row { + return table.Row{ + repo.renderName(), + repo.renderIsPrivate(), + repo.renderstatsDownloads(), + repo.renderstatsStars(), + repo.renderLastUpdate(), + repo.renderCreatedAt(), + } +} + +func (repo Repository) renderName() string { + return repo.Data.Name +} + +func (repo Repository) renderIsPrivate() string { + if repo.Data.IsPrivate { + return lipgloss.NewStyle().Foreground(lipgloss.Color("#EF476F")).Render(constants.GlyphPrivate) + } else { + return lipgloss.NewStyle().Foreground(lipgloss.Color("#06D6A0")).Render(constants.GlyphPublic) + } +} + +func (repo Repository) renderstatsDownloads() string { + return lipgloss.NewStyle(). + Render(fmt.Sprint(repo.Data.PullCount)) +} + +func (repo Repository) renderstatsStars() string { + return lipgloss.NewStyle(). + Render(fmt.Sprint(repo.Data.StarCount)) +} + +func (repo Repository) renderLastUpdate() string { + return lipgloss.NewStyle(). + Render(utils.TimeElapsed(repo.Data.UpdatedAt)) +} + +func (repo Repository) renderCreatedAt() string { + return lipgloss.NewStyle(). + Render(utils.TimeElapsed(repo.Data.CreatedAt)) +} diff --git a/internal/ui/components/sidebar/repository/sidebar_repository.go b/internal/ui/components/sidebar/explore/explore.go similarity index 81% rename from internal/ui/components/sidebar/repository/sidebar_repository.go rename to internal/ui/components/sidebar/explore/explore.go index f77072b..8d08322 100644 --- a/internal/ui/components/sidebar/repository/sidebar_repository.go +++ b/internal/ui/components/sidebar/explore/explore.go @@ -1,24 +1,24 @@ -package sidebar_repository +package explore_sidebar import ( "fmt" "github.com/charmbracelet/lipgloss" - "github.com/victorbersy/docker-hub-cli/internal/data" - "github.com/victorbersy/docker-hub-cli/internal/ui/components/repository" + data_search "github.com/victorbersy/docker-hub-cli/internal/data/search" + repository_search "github.com/victorbersy/docker-hub-cli/internal/ui/components/repository/search" ) type Model struct { - repo *repository.Repository + repo *repository_search.Repository width int } -func NewModel(data *data.RepositoryData, width int) Model { - var r *repository.Repository +func NewModel(data *data_search.Repository, width int) Model { + var r *repository_search.Repository if data == nil { r = nil } else { - r = &repository.Repository{Data: *data} + r = &repository_search.Repository{Data: *data} } return Model{ repo: r, diff --git a/internal/ui/components/sidebar/repository/styles.go b/internal/ui/components/sidebar/explore/styles.go similarity index 97% rename from internal/ui/components/sidebar/repository/styles.go rename to internal/ui/components/sidebar/explore/styles.go index 2a2c20f..aa1ea75 100644 --- a/internal/ui/components/sidebar/repository/styles.go +++ b/internal/ui/components/sidebar/explore/styles.go @@ -1,4 +1,4 @@ -package sidebar_repository +package explore_sidebar import ( "github.com/charmbracelet/lipgloss" diff --git a/internal/ui/components/sidebar/my_repos/my_repos.go b/internal/ui/components/sidebar/my_repos/my_repos.go new file mode 100644 index 0000000..a050c5f --- /dev/null +++ b/internal/ui/components/sidebar/my_repos/my_repos.go @@ -0,0 +1,92 @@ +package my_repos_sidebar + +import ( + "fmt" + + "github.com/charmbracelet/lipgloss" + data_user "github.com/victorbersy/docker-hub-cli/internal/data/user" + repository_user "github.com/victorbersy/docker-hub-cli/internal/ui/components/repository/user" + "github.com/victorbersy/docker-hub-cli/internal/ui/constants" + "github.com/victorbersy/docker-hub-cli/internal/utils" +) + +type Model struct { + repo *repository_user.Repository + width int +} + +func NewModel(data *data_user.Repository, width int) Model { + var r *repository_user.Repository + if data == nil { + r = nil + } else { + r = &repository_user.Repository{Data: *data} + } + return Model{ + repo: r, + width: width, + } +} + +func (m Model) View() string { + return lipgloss.JoinVertical( + lipgloss.Left, + m.renderTitle(), + m.renderVisibility(), + m.renderStats(), + m.renderTimestamps(), + ) +} + +func (m *Model) renderTitle() string { + return lipgloss.JoinHorizontal( + lipgloss.Center, + m.renderName(), + ) +} + +func (m *Model) renderName() string { + return titleRepo.Render(m.repo.Data.Name) +} + +func (m *Model) renderVisibility() string { + var visibility string + if m.repo.Data.IsPrivate { + visibility = lipgloss.NewStyle().Foreground(lipgloss.Color("#EF476F")).Padding(1, 0, 2, 0).Render(fmt.Sprintf("%s Private", constants.GlyphPrivate)) + } else { + visibility = lipgloss.NewStyle().Foreground(lipgloss.Color("#06D6A0")).Padding(1, 0, 2, 0).Render(fmt.Sprintf("%s Public", constants.GlyphPublic)) + } + return lipgloss.JoinVertical( + lipgloss.Top, + lipgloss.NewStyle().Bold(true).Underline(true).Render("Visibility:"), + visibility, + ) +} + +func (m *Model) renderStats() string { + return lipgloss.JoinVertical( + lipgloss.Top, + lipgloss.NewStyle().Bold(true).Underline(true).PaddingBottom(1).Render("Stats:"), + lipgloss.JoinHorizontal( + lipgloss.Top, + statsStars.Render(constants.GlyphStatsStars), + fmt.Sprint(m.repo.Data.StarCount), + ), + lipgloss.JoinHorizontal( + lipgloss.Top, + statsDownloads.Render(constants.GlyphStatsDownloads), + fmt.Sprint(m.repo.Data.PullCount), + ), + ) +} + +func (m *Model) renderTimestamps() string { + updated_at := lipgloss.NewStyle().Bold(true).Render("Last update:") + created_at := lipgloss.NewStyle().Bold(true).Render("Created at:") + return lipgloss.JoinVertical( + lipgloss.Top, + lipgloss.NewStyle().Bold(true).Underline(true).Padding(1, 0).Render("Timestamps:"), + lipgloss.NewStyle().Render(fmt.Sprintf("%s %s", updated_at, utils.TimeElapsed(m.repo.Data.UpdatedAt))), + lipgloss.NewStyle().Render(fmt.Sprintf("%s %s", created_at, utils.TimeElapsed(m.repo.Data.CreatedAt))), + ) +} diff --git a/internal/ui/components/sidebar/my_repos/styles.go b/internal/ui/components/sidebar/my_repos/styles.go new file mode 100644 index 0000000..ba9e848 --- /dev/null +++ b/internal/ui/components/sidebar/my_repos/styles.go @@ -0,0 +1,16 @@ +package my_repos_sidebar + +import ( + "github.com/charmbracelet/lipgloss" +) + +var ( + title = lipgloss.NewStyle().Bold(true).MarginBottom(1) + titleRepo = title.Copy().Margin(0, 1, 1, 0) + + statsDownloadsColors = lipgloss.AdaptiveColor{Light: "#00BBF9", Dark: "#00BBF9"} + statsStarsColors = lipgloss.AdaptiveColor{Light: "#FFB703", Dark: "#FFB703"} + + statsDownloads = lipgloss.NewStyle().Bold(true).PaddingRight(2).Foreground(statsDownloadsColors) + statsStars = lipgloss.NewStyle().Bold(true).PaddingRight(2).Foreground(statsStarsColors) +) diff --git a/internal/ui/components/view/explore/explore.go b/internal/ui/components/view/explore/explore.go index 9980375..80a83b7 100644 --- a/internal/ui/components/view/explore/explore.go +++ b/internal/ui/components/view/explore/explore.go @@ -5,7 +5,8 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/victorbersy/docker-hub-cli/internal/data" - "github.com/victorbersy/docker-hub-cli/internal/ui/components/repository" + data_search "github.com/victorbersy/docker-hub-cli/internal/data/search" + repository_search "github.com/victorbersy/docker-hub-cli/internal/ui/components/repository/search" "github.com/victorbersy/docker-hub-cli/internal/ui/components/table" "github.com/victorbersy/docker-hub-cli/internal/ui/components/view" "github.com/victorbersy/docker-hub-cli/internal/ui/constants" @@ -16,13 +17,13 @@ import ( const ViewType = "explore" type Model struct { - Repositories []data.RepositoryData + Repositories []data_search.Repository view view.Model } func NewModel(id int, ctx *context.ProgramContext) Model { m := Model{ - Repositories: []data.RepositoryData{}, + Repositories: []data_search.Repository{}, view: view.Model{ Id: id, Ctx: ctx, @@ -132,7 +133,7 @@ func (m *Model) GetViewColumns() []table.Column { func (m *Model) BuildRows() []table.Row { var rows []table.Row for _, currRepo := range m.Repositories { - repoModel := repository.Repository{Data: currRepo} + repoModel := repository_search.Repository{Data: currRepo} rows = append(rows, repoModel.ToTableRow()) } @@ -145,7 +146,7 @@ func (m *Model) NumRows() int { type ViewRepositoriesFetchedMsg struct { ViewId int - Repositories []data.RepositoryData + Repositories []data_search.Repository } func (msg ViewRepositoriesFetchedMsg) GetViewId() int { @@ -192,11 +193,11 @@ func (m *Model) FetchViewRows() tea.Cmd { cmds = append(cmds, m.view.CreateNextTickCmd(spinner.Tick)) cmds = append(cmds, func() tea.Msg { - fetchedRepos, err := data.FetchRepositories() + fetchedRepos, err := data_search.FetchRepositories() if err != nil { return ViewRepositoriesFetchedMsg{ ViewId: m.view.Id, - Repositories: []data.RepositoryData{}, + Repositories: []data_search.Repository{}, } } diff --git a/internal/ui/components/view/my_repos/my_repos.go b/internal/ui/components/view/my_repos/my_repos.go index d8ec6fc..ecc3856 100644 --- a/internal/ui/components/view/my_repos/my_repos.go +++ b/internal/ui/components/view/my_repos/my_repos.go @@ -1,13 +1,12 @@ package my_repos import ( - "time" - "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/victorbersy/docker-hub-cli/internal/data" - "github.com/victorbersy/docker-hub-cli/internal/ui/components/repository" + data_user "github.com/victorbersy/docker-hub-cli/internal/data/user" + repository_user "github.com/victorbersy/docker-hub-cli/internal/ui/components/repository/user" "github.com/victorbersy/docker-hub-cli/internal/ui/components/table" "github.com/victorbersy/docker-hub-cli/internal/ui/components/view" "github.com/victorbersy/docker-hub-cli/internal/ui/constants" @@ -18,13 +17,13 @@ import ( const ViewType = "my_repos" type Model struct { - Repositories []data.RepositoryData + Repositories []data_user.Repository view view.Model } func NewModel(id int, ctx *context.ProgramContext) Model { m := Model{ - Repositories: []data.RepositoryData{}, + Repositories: []data_user.Repository{}, view: view.Model{ Id: id, Ctx: ctx, @@ -39,7 +38,13 @@ func NewModel(id int, ctx *context.ProgramContext) Model { m.GetViewColumns(), m.BuildRows(), "Repositories", - utils.StringPtr(emptyStateStyle.Render("Nothing found")), + utils.StringPtr( + lipgloss.JoinVertical( + lipgloss.Top, + emptyStateStyle.Render("No repositories found."), + emptyStateStyle.Render("Have you set DOCKER_USERNAME and DOCKER_BEARER?"), + ), + ), ) return m @@ -93,15 +98,11 @@ func (m *Model) GetViewColumns() []table.Column { return []table.Column{ { Title: view.ColumnTitle.Render("Name"), - Width: &nameWidth, - }, - { - Title: "TODO", - Width: &labelsWidth, + Grow: utils.BoolPtr(true), }, { - Title: view.ColumnTitle.Render("Organization"), - Width: &organizationsnameWidth, + Title: columnTitleIsPrivate.Render(constants.GlyphIsPrivate), + Width: &isPrivateWidth, }, { Title: columnTitleStatsDownloads.Render(constants.GlyphStatsDownloads), @@ -112,12 +113,12 @@ func (m *Model) GetViewColumns() []table.Column { Width: &statsWidth, }, { - Title: view.ColumnTitle.Render("Updated At"), - Width: &LastUpdateCellWidth, + Title: view.ColumnTitle.Render("Last Update"), + Width: &updatedAtWidth, }, { - Title: view.ColumnTitle.Render("Description"), - Grow: utils.BoolPtr(true), + Title: view.ColumnTitle.Render("Created"), + Width: &createdAtWidth, }, } } @@ -125,7 +126,7 @@ func (m *Model) GetViewColumns() []table.Column { func (m *Model) BuildRows() []table.Row { var rows []table.Row for _, currRepo := range m.Repositories { - repoModel := repository.Repository{Data: currRepo} + repoModel := repository_user.Repository{Data: currRepo} rows = append(rows, repoModel.ToTableRow()) } @@ -138,7 +139,7 @@ func (m *Model) NumRows() int { type ViewRepositoriesFetchedMsg struct { ViewId int - Repositories []data.RepositoryData + Repositories []data_user.Repository } func (msg ViewRepositoriesFetchedMsg) GetViewId() int { @@ -184,51 +185,14 @@ func (m *Model) FetchViewRows() tea.Cmd { var cmds []tea.Cmd cmds = append(cmds, m.view.CreateNextTickCmd(spinner.Tick)) - fake_repository := data.RepositoryData{ - Architectures: []data.Architecture{ - { - Name: "My architecture", - Label: "Test", - }, - }, - Categories: []data.Category{ - { - Name: "My category", - Label: "Test", - }, - }, - CertificationStatus: "", - Created_at: time.Now(), - Description: "This is my very own Docker repository", - FilterType: "", - Name: "my-own-repo", - OperatingSystems: []data.OperatingSystem{ - { - Name: "linux", - Label: "linux", - }, - }, - Publisher: data.Publisher{ - Id: "myself", - Name: "myself", - }, - PullCount: "123", - Slug: "my-own-repo", - Source: "community", - StarCount: 456, - Type: ViewType, - Updated_at: time.Now(), - Labels: []data.Label{ - { - Name: "my-label", - Glyph: "X", - Color: lipgloss.AdaptiveColor{Light: "#F0F", Dark: "#FF0"}, - Enabled: false, - }, - }, - } cmds = append(cmds, func() tea.Msg { - fetchedRepos := []data.RepositoryData{fake_repository, fake_repository, fake_repository, fake_repository} + fetchedRepos, err := data_user.FetchRepositories() + if err != nil { + return ViewRepositoriesFetchedMsg{ + ViewId: m.view.Id, + Repositories: []data_user.Repository{}, + } + } return ViewRepositoriesFetchedMsg{ ViewId: m.view.Id, diff --git a/internal/ui/components/view/my_repos/styles.go b/internal/ui/components/view/my_repos/styles.go index ea66322..06d4adb 100644 --- a/internal/ui/components/view/my_repos/styles.go +++ b/internal/ui/components/view/my_repos/styles.go @@ -6,15 +6,16 @@ import ( ) var ( - nameWidth = 25 - organizationsnameWidth = 20 - LastUpdateCellWidth = lipgloss.Width(" Last Update ") - labelsWidth = 12 - statsWidth = 8 + isPrivateWidth = 4 + updatedAtWidth = lipgloss.Width(" Last Update ") + createdAtWidth = lipgloss.Width(" Created at ") + statsWidth = 8 + isPrivate = lipgloss.AdaptiveColor{Light: "#edede9", Dark: "#edede9"} statsDownloads = lipgloss.AdaptiveColor{Light: "#00BBF9", Dark: "#00BBF9"} statsStars = lipgloss.AdaptiveColor{Light: "#FFB703", Dark: "#FFB703"} + columnTitleIsPrivate = view.ColumnTitle.Copy().Foreground(isPrivate) columnTitleStatsDownloads = view.ColumnTitle.Copy().Foreground(statsDownloads) columnTitleStatsStars = view.ColumnTitle.Copy().Foreground(statsStars) diff --git a/internal/ui/constants/constants.go b/internal/ui/constants/constants.go index 61069ac..2b76878 100644 --- a/internal/ui/constants/constants.go +++ b/internal/ui/constants/constants.go @@ -33,6 +33,9 @@ var ( GlyphLabelCommunity = "וֹ" GlyphStatsDownloads = "" GlyphStatsStars = "" + GlyphIsPrivate = "" + GlyphPrivate = "" + GlyphPublic = "" ColorLabelDockerOfficial = lipgloss.AdaptiveColor{Light: "#83c5be", Dark: "#2E7F74"} ColorLabelVerifiedPublisher = lipgloss.AdaptiveColor{Light: "#48cae4", Dark: "#086DD7"} diff --git a/internal/ui/ui_model.go b/internal/ui/ui_model.go index 80da8e1..c639084 100644 --- a/internal/ui/ui_model.go +++ b/internal/ui/ui_model.go @@ -4,10 +4,12 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/victorbersy/docker-hub-cli/internal/config" - "github.com/victorbersy/docker-hub-cli/internal/data" + data_search "github.com/victorbersy/docker-hub-cli/internal/data/search" + data_user "github.com/victorbersy/docker-hub-cli/internal/data/user" "github.com/victorbersy/docker-hub-cli/internal/ui/components/help" "github.com/victorbersy/docker-hub-cli/internal/ui/components/sidebar" - sidebar_repository "github.com/victorbersy/docker-hub-cli/internal/ui/components/sidebar/repository" + explore_sidebar "github.com/victorbersy/docker-hub-cli/internal/ui/components/sidebar/explore" + my_repos_sidebar "github.com/victorbersy/docker-hub-cli/internal/ui/components/sidebar/my_repos" "github.com/victorbersy/docker-hub-cli/internal/ui/components/tabs" "github.com/victorbersy/docker-hub-cli/internal/ui/components/view" "github.com/victorbersy/docker-hub-cli/internal/ui/context" @@ -52,17 +54,13 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch { case key.Matches(msg, m.keys.PrevView): prevView := m.getViewAt(m.getPrevViewId()) - if prevView != nil { - m.ctx.View = m.switchSelectedView() - m.setCurrentView(prevView) - } + m.ctx.View = m.switchSelectedView() + m.setCurrentView(prevView) case key.Matches(msg, m.keys.NextView): nextView := m.getViewAt(m.getNextViewId()) - if nextView != nil { - m.ctx.View = m.switchSelectedView() - m.setCurrentView(nextView) - } + m.ctx.View = m.switchSelectedView() + m.setCurrentView(nextView) case key.Matches(msg, m.keys.Down): currView.NextRow() @@ -127,7 +125,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m *Model) onViewedRowChanged() { - m.syncSidebarExplore() + m.syncSidebar() } func (m *Model) onWindowSizeChanged(msg tea.WindowSizeMsg) { @@ -154,6 +152,19 @@ func (m *Model) updateRelevantView(msg view.ViewMsg) (cmd tea.Cmd) { return cmd } +func (m *Model) syncSidebar() { + currRowData := m.getCurrRowData() + width := m.sidebar.GetSidebarContentWidth() + switch data := currRowData.(type) { + case *data_search.Repository: + content := explore_sidebar.NewModel(data, width).View() + m.sidebar.SetContent(content) + case *data_user.Repository: + content := my_repos_sidebar.NewModel(data, width).View() + m.sidebar.SetContent(content) + } +} + func (m *Model) syncMainContentWidth() { sideBarOffset := 0 if m.sidebar.IsOpen { @@ -161,14 +172,3 @@ func (m *Model) syncMainContentWidth() { } m.ctx.MainContentWidth = m.ctx.ScreenWidth - sideBarOffset } - -func (m *Model) syncSidebarExplore() { - currRowData := m.getCurrRowData() - width := m.sidebar.GetSidebarContentWidth() - - switch row_data := currRowData.(type) { - case *data.RepositoryData: - content := sidebar_repository.NewModel(row_data, width).View() - m.sidebar.SetContent(content) - } -} diff --git a/main.go b/main.go index 34da51e..e11dc08 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "log" "os" @@ -13,8 +12,7 @@ func main() { if len(os.Getenv("DEBUG")) > 0 { f, err := tea.LogToFile("debug.log", "debug") if err != nil { - fmt.Println("fatal:", err) - os.Exit(1) + log.Fatal("fatal:", err) } defer f.Close() }