package main import ( "fmt" "os" "strings" "time" "github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/table" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "code.gitea.io/sdk/gitea" ) var textStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#5b6078")) type timesviewer struct { table table.Model length int total_time time.Duration } func (m timesviewer) Init() tea.Cmd { return nil } func (m timesviewer) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "esc": if m.table.Focused() { m.table.Blur() } else { m.table.Focus() } case "q", "ctrl+c": return m, tea.Quit } } m.table, cmd = m.table.Update(msg) return m, cmd } func (m timesviewer) View() string { var totalTextStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("#6e738d")). Bold(true) return m.table.View() + textStyle.Render("\nUse Up and Down arrows to navigate") + totalTextStyle.Render(fmt.Sprintf("\nTotal - %s\n", m.total_time.String())) } type Indicator struct { spinner spinner.Model quitting bool err error info IndicatorInfo } type IndicatorInfo struct { duration time.Duration quitting bool info string } func (i IndicatorInfo) String() string { if i.duration == 0 { return textStyle.Render(strings.Repeat(".", 30)) } return fmt.Sprintf("%s %s", textStyle.Render(i.duration.String()), i.info) } func initialIndicator() Indicator { s := spinner.New() s.Spinner = spinner.Dot s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("#a6da95")) return Indicator{spinner: s} } func (m Indicator) Init() tea.Cmd { return m.spinner.Tick } func (m Indicator) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case error: m.err = msg return m, nil case IndicatorInfo: m.info = msg m.quitting = msg.quitting return m, nil default: var cmd tea.Cmd m.spinner, cmd = m.spinner.Update(msg) return m, cmd } } func (m Indicator) View() string { if m.err != nil { return m.err.Error() } str := "" if !m.quitting { str += fmt.Sprintf("\n %s Loading time logs...\n %s\n", m.spinner.View(), m.info.String()) } return str } func times() { p := tea.NewProgram(initialIndicator()) go func() { if _, err := p.Run(); err != nil { fmt.Println(err) os.Exit(1) } }() var times []gitea.TrackedTime since := time.Now().AddDate(0, 0, -7) for _, client := range Clients { page := 1 user, _, err := client.GetMyUserInfo() if err != nil { panic(err) } for { repos, _, err := client.ListMyRepos(gitea.ListReposOptions{ListOptions: gitea.ListOptions{Page: page, PageSize: 10}}) if err != nil { panic(err) } else if len(repos) == 0 { break } for _, repo := range repos { if repo.Fork { continue } duration_start := time.Now() repo_times, _, _ := client.ListRepoTrackedTimes( repo.Owner.UserName, repo.Name, gitea.ListTrackedTimesOptions{ User: user.UserName, Since: since, }, ) for _, t := range repo_times { times = append(times, *t) } p.Send(IndicatorInfo{ info: fmt.Sprintf("%s / %s", repo.Owner.UserName, repo.Name), duration: time.Now().Sub(duration_start), }) } page++ } } p.Send(IndicatorInfo{ info: "Done!", quitting: true, }) p.Quit() var total_time time.Duration columns := []table.Column{ {Title: "User", Width: 10}, {Title: "Time", Width: 8}, {Title: "Created at", Width: 15}, } rows := []table.Row{} for _, t := range times { dur, err := time.ParseDuration(fmt.Sprint(t.Time) + "s") if err != nil { panic(err) } rows = append(rows, table.Row{ t.UserName, dur.String(), t.Created.String(), }) total_time += dur } tv := timesviewer{ total_time: total_time, length: 50, } tab := table.New( table.WithColumns(columns), table.WithRows(rows), table.WithFocused(true), table.WithHeight(10), table.WithWidth(tv.length), ) s := table.DefaultStyles() s.Header = s.Header. Foreground(lipgloss.Color("#6e738d")). BorderStyle(lipgloss.DoubleBorder()). BorderForeground(lipgloss.Color("#a6da95")). BorderBottom(true). Bold(false) s.Selected = s.Selected. Foreground(lipgloss.Color("#494d64")). Background(lipgloss.Color("#a6da95")). Bold(false) tab.SetStyles(s) tv.table = tab if _, err := tea.NewProgram(tv).Run(); err != nil { fmt.Println("Error running program:", err) os.Exit(1) } }