package main import ( "fmt" "os" "sort" "time" "github.com/charmbracelet/bubbles/table" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "code.gitea.io/sdk/gitea" ) func times() { finished := false p := tea.NewProgram(initialIndicator("Fetching time logs...")) go func() { if _, err := p.Run(); err != nil { fmt.Println(err) } // If the indicator exited before we got all the time logs, it was either an error or the user press CTRL + C / q if !finished { os.Exit(1) } }() since := time.Now().AddDate(0, -6, 0) times := getTimeLogs(since, func(repo gitea.Repository, took time.Duration) { p.Send(IndicatorInfo{ info: fmt.Sprintf("%s / %s", repo.Owner.UserName, repo.Name), duration: took, }) }) p.Send(IndicatorInfo{ info: "Done!", quitting: true, }) finished = true p.Quit() var total_time time.Duration columns := []table.Column{ {Title: "Server", Width: 10}, {Title: "User", Width: 8}, {Title: "Time", Width: 8}, {Title: "Created at", Width: 11}, } 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.server.servername, t.UserName, dur.String(), t.Created.Format(time.DateOnly), }) 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(colors.overlay[0]). BorderStyle(lipgloss.DoubleBorder()). BorderForeground(colors.green). BorderBottom(true). Bold(false) s.Selected = s.Selected. Foreground(colors.surface[1]). Background(colors.green). 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) } } // TrackedTime is an extended gitea.TrackedTime struct type TrackedTime struct { gitea.TrackedTime server Server } // getTimeLogs gets every single time log possible. func getTimeLogs(since time.Time, on_process_repo func(repo gitea.Repository, took time.Duration)) []TrackedTime { var times []TrackedTime for server_name, client := range Servers { 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, TrackedTime{TrackedTime: *t, server: *config.servers[server_name]}) } on_process_repo(*repo, time.Now().Sub(duration_start)) } page++ } } // Sort the times by Created At sort.SliceStable(times, func(i, j int) bool { return times[j].Created.Compare(times[i].Created) < 1 }) return times } 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(colors.overlay[0]). Bold(true) return m.table.View() + styles.text.Render("\nUse Up and Down arrows to navigate") + totalTextStyle.Render(fmt.Sprintf("\nTotal - %s\n", m.total_time.String())) }