forked from tangled.org/core
this repo has no description
1package repo 2 3import ( 4 "context" 5 "fmt" 6 "log" 7 "net/http" 8 "slices" 9 "time" 10 11 "tangled.sh/tangled.sh/core/appview/db" 12 "tangled.sh/tangled.sh/core/appview/reporesolver" 13 14 "github.com/bluesky-social/indigo/atproto/syntax" 15 "github.com/gorilla/feeds" 16) 17 18func (rp *Repo) getRepoFeed(ctx context.Context, f *reporesolver.ResolvedRepo) (*feeds.Feed, error) { 19 const feedLimitPerType = 100 20 21 pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", f.RepoAt())) 22 if err != nil { 23 return nil, err 24 } 25 26 issues, err := db.GetIssuesWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", f.RepoAt())) 27 if err != nil { 28 return nil, err 29 } 30 31 feed := &feeds.Feed{ 32 Title: fmt.Sprintf("activity feed for %s", f.OwnerSlashRepo()), 33 Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.AppviewHost, f.OwnerSlashRepo()), Type: "text/html", Rel: "alternate"}, 34 Items: make([]*feeds.Item, 0), 35 Updated: time.UnixMilli(0), 36 } 37 38 for _, pull := range pulls { 39 items, err := rp.createPullItems(ctx, pull, f) 40 if err != nil { 41 return nil, err 42 } 43 feed.Items = append(feed.Items, items...) 44 } 45 46 for _, issue := range issues { 47 item, err := rp.createIssueItem(ctx, issue, f) 48 if err != nil { 49 return nil, err 50 } 51 feed.Items = append(feed.Items, item) 52 } 53 54 slices.SortFunc(feed.Items, func(a, b *feeds.Item) int { 55 if a.Created.After(b.Created) { 56 return -1 57 } 58 return 1 59 }) 60 61 if len(feed.Items) > 0 { 62 feed.Updated = feed.Items[0].Created 63 } 64 65 return feed, nil 66} 67 68func (rp *Repo) createPullItems(ctx context.Context, pull *db.Pull, f *reporesolver.ResolvedRepo) ([]*feeds.Item, error) { 69 owner, err := rp.idResolver.ResolveIdent(ctx, pull.OwnerDid) 70 if err != nil { 71 return nil, err 72 } 73 74 var items []*feeds.Item 75 76 state := rp.getPullState(pull) 77 description := rp.buildPullDescription(owner.Handle, state, pull, f.OwnerSlashRepo()) 78 79 mainItem := &feeds.Item{ 80 Title: fmt.Sprintf("[PR #%d] %s", pull.PullId, pull.Title), 81 Description: description, 82 Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId)}, 83 Created: pull.Created, 84 Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)}, 85 } 86 items = append(items, mainItem) 87 88 for _, round := range pull.Submissions { 89 if round == nil || round.RoundNumber == 0 { 90 continue 91 } 92 93 roundItem := &feeds.Item{ 94 Title: fmt.Sprintf("[PR #%d] %s (round #%d)", pull.PullId, pull.Title, round.RoundNumber), 95 Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in %s", owner.Handle, round.RoundNumber, pull.PullId, f.OwnerSlashRepo()), 96 Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId, round.RoundNumber)}, 97 Created: round.Created, 98 Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)}, 99 } 100 items = append(items, roundItem) 101 } 102 103 return items, nil 104} 105 106func (rp *Repo) createIssueItem(ctx context.Context, issue db.Issue, f *reporesolver.ResolvedRepo) (*feeds.Item, error) { 107 owner, err := rp.idResolver.ResolveIdent(ctx, issue.Did) 108 if err != nil { 109 return nil, err 110 } 111 112 state := "closed" 113 if issue.Open { 114 state = "opened" 115 } 116 117 return &feeds.Item{ 118 Title: fmt.Sprintf("[Issue #%d] %s", issue.IssueId, issue.Title), 119 Description: fmt.Sprintf("@%s %s issue #%d in %s", owner.Handle, state, issue.IssueId, f.OwnerSlashRepo()), 120 Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), issue.IssueId)}, 121 Created: issue.Created, 122 Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)}, 123 }, nil 124} 125 126func (rp *Repo) getPullState(pull *db.Pull) string { 127 if pull.State == db.PullOpen { 128 return "opened" 129 } 130 return pull.State.String() 131} 132 133func (rp *Repo) buildPullDescription(handle syntax.Handle, state string, pull *db.Pull, repoName string) string { 134 base := fmt.Sprintf("@%s %s pull request #%d", handle, state, pull.PullId) 135 136 if pull.State == db.PullMerged { 137 return fmt.Sprintf("%s (on round #%d) in %s", base, pull.LastRoundNumber(), repoName) 138 } 139 140 return fmt.Sprintf("%s in %s", base, repoName) 141} 142 143func (rp *Repo) RepoAtomFeed(w http.ResponseWriter, r *http.Request) { 144 f, err := rp.repoResolver.Resolve(r) 145 if err != nil { 146 log.Println("failed to fully resolve repo:", err) 147 return 148 } 149 150 feed, err := rp.getRepoFeed(r.Context(), f) 151 if err != nil { 152 log.Println("failed to get repo feed:", err) 153 rp.pages.Error500(w) 154 return 155 } 156 157 atom, err := feed.ToAtom() 158 if err != nil { 159 rp.pages.Error500(w) 160 return 161 } 162 163 w.Header().Set("content-type", "application/atom+xml") 164 w.Write([]byte(atom)) 165}