rin
Публичный Open-source lightweight git hosting.
activity.go · 136 строк · 3.5 KB
Raw
1 package repo
2
3 import (
4 "context"
5 "fmt"
6 "sort"
7 "time"
8
9 "polar/internal/git"
10 )
11
12 type ActivityItem struct {
13 Type string // "commit", "pull", "issue", "release"
14 Title string
15 Subtitle string
16 AuthorUsername string
17 AuthorAvatar string
18 Link string
19 Icon string
20 Badge string
21 CreatedAt time.Time
22 }
23
24 type ActivityStats struct {
25 CommitCount int
26 OpenIssues int
27 ClosedIssues int
28 OpenPRs int
29 MergedPRs int
30 ReleaseCount int
31 }
32
33 func (s *Service) GetRepoActivity(ctx context.Context, r *Repository, diskPath string) ([]ActivityItem, *ActivityStats, error) {
34 var items []ActivityItem
35
36 // 1. Recent commits from default branch
37 commits, _ := git.ListCommits(ctx, diskPath, r.DefaultBranch, 25, 0)
38 for _, c := range commits {
39 items = append(items, ActivityItem{
40 Type: "commit",
41 Title: c.Subject,
42 Subtitle: c.ShortHash,
43 AuthorUsername: c.AuthorName,
44 AuthorAvatar: "",
45 Link: fmt.Sprintf("/%s/%s/commit/%s", r.OwnerUsername, r.Name, c.Hash),
46 Icon: "git-commit",
47 Badge: "commit",
48 CreatedAt: c.AuthoredAt,
49 })
50 }
51
52 // 2. Recent Pull Requests
53 pulls, _ := s.ListPullRequests(ctx, r.ID, "all")
54 var openPRs, mergedPRs int
55 for _, pr := range pulls {
56 switch pr.Status {
57 case "open":
58 openPRs++
59 case "merged":
60 mergedPRs++
61 }
62 badge := pr.Status
63 icon := "git-pull-request"
64 items = append(items, ActivityItem{
65 Type: "pull",
66 Title: fmt.Sprintf("#%d %s", pr.Number, pr.Title),
67 Subtitle: fmt.Sprintf("%s → %s", pr.HeadBranch, pr.BaseBranch),
68 AuthorUsername: pr.AuthorUsername,
69 AuthorAvatar: pr.AuthorAvatar,
70 Link: fmt.Sprintf("/%s/%s/pulls/%d", r.OwnerUsername, r.Name, pr.Number),
71 Icon: icon,
72 Badge: badge,
73 CreatedAt: pr.CreatedAt,
74 })
75 }
76
77 // 3. Recent Issues
78 openIssList, _ := s.ListIssues(ctx, r.ID, false)
79 closedIssList, _ := s.ListIssues(ctx, r.ID, true)
80 openIssues := len(openIssList)
81 closedIssues := len(closedIssList)
82 allIssues := append(openIssList, closedIssList...)
83 for _, iss := range allIssues {
84 badge := "open"
85 if iss.IsClosed {
86 badge = "closed"
87 }
88 items = append(items, ActivityItem{
89 Type: "issue",
90 Title: fmt.Sprintf("#%d %s", iss.Number, iss.Title),
91 Subtitle: "",
92 AuthorUsername: iss.AuthorUsername,
93 AuthorAvatar: iss.AuthorAvatar,
94 Link: fmt.Sprintf("/%s/%s/issues/%d", r.OwnerUsername, r.Name, iss.Number),
95 Icon: "issue",
96 Badge: badge,
97 CreatedAt: iss.CreatedAt,
98 })
99 }
100
101 // 4. Recent Releases
102 releases, _ := s.ListReleases(ctx, r.ID)
103 for _, rel := range releases {
104 items = append(items, ActivityItem{
105 Type: "release",
106 Title: fmt.Sprintf("%s (%s)", rel.Title, rel.TagName),
107 Subtitle: "",
108 AuthorUsername: rel.AuthorUsername,
109 AuthorAvatar: rel.AuthorAvatar,
110 Link: fmt.Sprintf("/%s/%s/releases#%s", r.OwnerUsername, r.Name, rel.TagName),
111 Icon: "tag",
112 Badge: "release",
113 CreatedAt: rel.PublishedAt,
114 })
115 }
116
117 // Sort combined timeline in reverse chronological order
118 sort.Slice(items, func(i, j int) bool {
119 return items[i].CreatedAt.After(items[j].CreatedAt)
120 })
121
122 if len(items) > 50 {
123 items = items[:50]
124 }
125
126 stats := &ActivityStats{
127 CommitCount: len(commits),
128 OpenIssues: openIssues,
129 ClosedIssues: closedIssues,
130 OpenPRs: openPRs,
131 MergedPRs: mergedPRs,
132 ReleaseCount: len(releases),
133 }
134
135 return items, stats, nil
136 }
137