Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

appview: repo: implement generating feed

Signed-off-by: dusk <y.bera003.06@protonmail.com>

authored by

dusk and committed by
Tangled
61c4ddbc f5917160

+123
+122
appview/repo/repo.go
··· 37 37 securejoin "github.com/cyphar/filepath-securejoin" 38 38 "github.com/go-chi/chi/v5" 39 39 "github.com/go-git/go-git/v5/plumbing" 40 + "github.com/gorilla/feeds" 40 41 41 42 comatproto "github.com/bluesky-social/indigo/api/atproto" 42 43 "github.com/bluesky-social/indigo/atproto/syntax" ··· 287 286 }) 288 287 return 289 288 } 289 + } 290 + 291 + func (rp *Repo) getRepoFeed(ctx context.Context, f *reporesolver.ResolvedRepo) (*feeds.Feed, error) { 292 + const feedLimitPerType = 100 293 + 294 + pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", f.RepoAt())) 295 + if err != nil { 296 + return nil, err 297 + } 298 + 299 + issues, err := db.GetIssuesWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", f.RepoAt())) 300 + if err != nil { 301 + return nil, err 302 + } 303 + 304 + feed := &feeds.Feed{ 305 + Title: fmt.Sprintf("activity feed for %s", f.OwnerSlashRepo()), 306 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.AppviewHost, f.OwnerSlashRepo()), Type: "text/html", Rel: "alternate"}, 307 + Items: make([]*feeds.Item, 0), 308 + Updated: time.UnixMilli(0), 309 + } 310 + 311 + for _, pull := range pulls { 312 + owner, err := rp.idResolver.ResolveIdent(ctx, pull.OwnerDid) 313 + if err != nil { 314 + return nil, err 315 + } 316 + 317 + var state string 318 + if pull.State == db.PullOpen { 319 + state = "opened" 320 + } else { 321 + state = pull.State.String() 322 + } 323 + mergedAtRounds := "" 324 + if pull.State == db.PullMerged { 325 + mergedAtRounds = fmt.Sprintf(" (on round #%d)", pull.LastRoundNumber()) 326 + } 327 + item := &feeds.Item{ 328 + Title: fmt.Sprintf("[PR #%d] %s", pull.PullId, pull.Title), 329 + Description: fmt.Sprintf("@%s %s pull request #%d%s in %s", owner.Handle, state, pull.PullId, mergedAtRounds, f.OwnerSlashRepo()), 330 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId)}, 331 + Created: pull.Created, 332 + Author: &feeds.Author{ 333 + Name: fmt.Sprintf("@%s", owner.Handle), 334 + }, 335 + } 336 + feed.Items = append(feed.Items, item) 337 + 338 + for _, round := range pull.Submissions { 339 + if round == nil || round.RoundNumber == 0 { 340 + continue 341 + } 342 + item := &feeds.Item{ 343 + Title: fmt.Sprintf("[PR #%d] %s (round #%d)", pull.PullId, pull.Title, round.RoundNumber), 344 + Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in %s", owner.Handle, round.RoundNumber, pull.PullId, f.OwnerSlashRepo()), 345 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId, round.RoundNumber)}, 346 + Created: round.Created, 347 + Author: &feeds.Author{ 348 + Name: fmt.Sprintf("@%s", owner.Handle), 349 + }, 350 + } 351 + feed.Items = append(feed.Items, item) 352 + } 353 + } 354 + 355 + for _, issue := range issues { 356 + owner, err := rp.idResolver.ResolveIdent(ctx, issue.OwnerDid) 357 + if err != nil { 358 + return nil, err 359 + } 360 + var state string 361 + if issue.Open { 362 + state = "opened" 363 + } else { 364 + state = "closed" 365 + } 366 + item := &feeds.Item{ 367 + Title: fmt.Sprintf("[Issue #%d] %s", issue.IssueId, issue.Title), 368 + Description: fmt.Sprintf("@%s %s issue #%d in %s", owner.Handle, state, issue.IssueId, f.OwnerSlashRepo()), 369 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), issue.IssueId)}, 370 + Created: issue.Created, 371 + Author: &feeds.Author{ 372 + Name: fmt.Sprintf("@%s", owner.Handle), 373 + }, 374 + } 375 + feed.Items = append(feed.Items, item) 376 + } 377 + 378 + slices.SortFunc(feed.Items, func(a *feeds.Item, b *feeds.Item) int { 379 + return int(b.Created.UnixMilli()) - int(a.Created.UnixMilli()) 380 + }) 381 + if len(feed.Items) > 0 { 382 + feed.Updated = feed.Items[0].Created 383 + } 384 + 385 + return feed, nil 386 + } 387 + 388 + func (rp *Repo) RepoAtomFeed(w http.ResponseWriter, r *http.Request) { 389 + f, err := rp.repoResolver.Resolve(r) 390 + if err != nil { 391 + log.Println("failed to fully resolve repo:", err) 392 + return 393 + } 394 + 395 + feed, err := rp.getRepoFeed(r.Context(), f) 396 + if err != nil { 397 + log.Println("failed to get repo feed:", err) 398 + rp.pages.Error500(w) 399 + return 400 + } 401 + 402 + atom, err := feed.ToAtom() 403 + if err != nil { 404 + rp.pages.Error500(w) 405 + return 406 + } 407 + 408 + w.Header().Set("content-type", "application/atom+xml") 409 + w.Write([]byte(atom)) 290 410 } 291 411 292 412 func (rp *Repo) RepoCommit(w http.ResponseWriter, r *http.Request) {
+1
appview/repo/router.go
··· 10 10 func (rp *Repo) Router(mw *middleware.Middleware) http.Handler { 11 11 r := chi.NewRouter() 12 12 r.Get("/", rp.RepoIndex) 13 + r.Get("/feed.atom", rp.RepoAtomFeed) 13 14 r.Get("/commits/{ref}", rp.RepoLog) 14 15 r.Route("/tree/{ref}", func(r chi.Router) { 15 16 r.Get("/", rp.RepoIndex)