forked from tangled.org/core
Monorepo for Tangled — https://tangled.org

appview: refactor profile feed generation

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 6a55af89 85d39c0c

verified
Changed files
+95 -63
appview
state
+95 -63
appview/state/profile.go
··· 175 }) 176 } 177 178 - func (s *State) feedFromRequest(w http.ResponseWriter, r *http.Request) *feeds.Feed { 179 ident, ok := r.Context().Value("resolvedId").(identity.Identity) 180 if !ok { 181 s.pages.Error404(w) 182 - return nil 183 } 184 185 - feed, err := s.GetProfileFeed(r.Context(), ident.Handle.String(), ident.DID.String()) 186 if err != nil { 187 s.pages.Error500(w) 188 - return nil 189 } 190 191 - return feed 192 - } 193 - 194 - func (s *State) AtomFeedPage(w http.ResponseWriter, r *http.Request) { 195 - feed := s.feedFromRequest(w, r) 196 if feed == nil { 197 return 198 } ··· 207 w.Write([]byte(atom)) 208 } 209 210 - func (s *State) GetProfileFeed(ctx context.Context, handle string, did string) (*feeds.Feed, error) { 211 - timeline, err := db.MakeProfileTimeline(s.db, did) 212 if err != nil { 213 return nil, err 214 } 215 216 author := &feeds.Author{ 217 - Name: fmt.Sprintf("@%s", handle), 218 } 219 - feed := &feeds.Feed{ 220 - Title: fmt.Sprintf("timeline feed for %s", author.Name), 221 - Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s", s.config.Core.AppviewHost, handle), Type: "text/html", Rel: "alternate"}, 222 Items: make([]*feeds.Item, 0), 223 Updated: time.UnixMilli(0), 224 Author: author, 225 } 226 for _, byMonth := range timeline.ByMonth { 227 - for _, pull := range byMonth.PullEvents.Items { 228 - owner, err := s.idResolver.ResolveIdent(ctx, pull.Repo.Did) 229 - if err != nil { 230 - return nil, err 231 - } 232 - feed.Items = append(feed.Items, &feeds.Item{ 233 - Title: fmt.Sprintf("%s created pull request '%s' in @%s/%s", author.Name, pull.Title, owner.Handle, pull.Repo.Name), 234 - Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/pulls/%d", s.config.Core.AppviewHost, owner.Handle, pull.Repo.Name, pull.PullId), Type: "text/html", Rel: "alternate"}, 235 - Created: pull.Created, 236 - Author: author, 237 - }) 238 - for _, submission := range pull.Submissions { 239 - feed.Items = append(feed.Items, &feeds.Item{ 240 - Title: fmt.Sprintf("%s submitted pull request '%s' (round #%d) in @%s/%s", author.Name, pull.Title, submission.RoundNumber, owner.Handle, pull.Repo.Name), 241 - Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/pulls/%d", s.config.Core.AppviewHost, owner.Handle, pull.Repo.Name, pull.PullId), Type: "text/html", Rel: "alternate"}, 242 - Created: submission.Created, 243 - Author: author, 244 - }) 245 - } 246 } 247 - for _, issue := range byMonth.IssueEvents.Items { 248 - owner, err := s.idResolver.ResolveIdent(ctx, issue.Metadata.Repo.Did) 249 - if err != nil { 250 - return nil, err 251 - } 252 - feed.Items = append(feed.Items, &feeds.Item{ 253 - Title: fmt.Sprintf("%s created issue '%s' in @%s/%s", author.Name, issue.Title, owner.Handle, issue.Metadata.Repo.Name), 254 - Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/issues/%d", s.config.Core.AppviewHost, owner.Handle, issue.Metadata.Repo.Name, issue.IssueId), Type: "text/html", Rel: "alternate"}, 255 - Created: issue.Created, 256 - Author: author, 257 - }) 258 } 259 - for _, repo := range byMonth.RepoEvents { 260 - var title string 261 - if repo.Source != nil { 262 - id, err := s.idResolver.ResolveIdent(ctx, repo.Source.Did) 263 - if err != nil { 264 - return nil, err 265 - } 266 - title = fmt.Sprintf("%s forked repository @%s/%s to '%s'", author.Name, id.Handle, repo.Source.Name, repo.Repo.Name) 267 - } else { 268 - title = fmt.Sprintf("%s created repository '%s'", author.Name, repo.Repo.Name) 269 - } 270 - feed.Items = append(feed.Items, &feeds.Item{ 271 - Title: title, 272 - Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s", s.config.Core.AppviewHost, handle, repo.Repo.Name), Type: "text/html", Rel: "alternate"}, 273 - Created: repo.Repo.Created, 274 - Author: author, 275 - }) 276 } 277 } 278 slices.SortFunc(feed.Items, func(a *feeds.Item, b *feeds.Item) int { 279 return int(b.Created.UnixMilli()) - int(a.Created.UnixMilli()) 280 }) 281 if len(feed.Items) > 0 { 282 feed.Updated = feed.Items[0].Created 283 } 284 285 - return feed, nil 286 } 287 288 func (s *State) UpdateProfileBio(w http.ResponseWriter, r *http.Request) {
··· 175 }) 176 } 177 178 + func (s *State) AtomFeedPage(w http.ResponseWriter, r *http.Request) { 179 ident, ok := r.Context().Value("resolvedId").(identity.Identity) 180 if !ok { 181 s.pages.Error404(w) 182 + return 183 } 184 185 + feed, err := s.getProfileFeed(r.Context(), &ident) 186 if err != nil { 187 s.pages.Error500(w) 188 + return 189 } 190 191 if feed == nil { 192 return 193 } ··· 202 w.Write([]byte(atom)) 203 } 204 205 + func (s *State) getProfileFeed(ctx context.Context, id *identity.Identity) (*feeds.Feed, error) { 206 + timeline, err := db.MakeProfileTimeline(s.db, id.DID.String()) 207 if err != nil { 208 return nil, err 209 } 210 211 author := &feeds.Author{ 212 + Name: fmt.Sprintf("@%s", id.Handle), 213 } 214 + 215 + feed := feeds.Feed{ 216 + Title: fmt.Sprintf("%s's timeline", author.Name), 217 + Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s", s.config.Core.AppviewHost, id.Handle), Type: "text/html", Rel: "alternate"}, 218 Items: make([]*feeds.Item, 0), 219 Updated: time.UnixMilli(0), 220 Author: author, 221 } 222 + 223 for _, byMonth := range timeline.ByMonth { 224 + if err := s.addPullRequestItems(ctx, &feed, byMonth.PullEvents.Items, author); err != nil { 225 + return nil, err 226 } 227 + if err := s.addIssueItems(ctx, &feed, byMonth.IssueEvents.Items, author); err != nil { 228 + return nil, err 229 } 230 + if err := s.addRepoItems(ctx, &feed, byMonth.RepoEvents, author); err != nil { 231 + return nil, err 232 } 233 } 234 + 235 slices.SortFunc(feed.Items, func(a *feeds.Item, b *feeds.Item) int { 236 return int(b.Created.UnixMilli()) - int(a.Created.UnixMilli()) 237 }) 238 + 239 if len(feed.Items) > 0 { 240 feed.Updated = feed.Items[0].Created 241 } 242 243 + return &feed, nil 244 + } 245 + 246 + func (s *State) addPullRequestItems(ctx context.Context, feed *feeds.Feed, pulls []*db.Pull, author *feeds.Author) error { 247 + for _, pull := range pulls { 248 + owner, err := s.idResolver.ResolveIdent(ctx, pull.Repo.Did) 249 + if err != nil { 250 + return err 251 + } 252 + 253 + // Add pull request creation item 254 + feed.Items = append(feed.Items, s.createPullRequestItem(pull, owner, author)) 255 + } 256 + return nil 257 + } 258 + 259 + func (s *State) addIssueItems(ctx context.Context, feed *feeds.Feed, issues []*db.Issue, author *feeds.Author) error { 260 + for _, issue := range issues { 261 + owner, err := s.idResolver.ResolveIdent(ctx, issue.Metadata.Repo.Did) 262 + if err != nil { 263 + return err 264 + } 265 + 266 + feed.Items = append(feed.Items, s.createIssueItem(issue, owner, author)) 267 + } 268 + return nil 269 + } 270 + 271 + func (s *State) addRepoItems(ctx context.Context, feed *feeds.Feed, repos []db.RepoEvent, author *feeds.Author) error { 272 + for _, repo := range repos { 273 + item, err := s.createRepoItem(ctx, repo, author) 274 + if err != nil { 275 + return err 276 + } 277 + feed.Items = append(feed.Items, item) 278 + } 279 + return nil 280 + } 281 + 282 + func (s *State) createPullRequestItem(pull *db.Pull, owner *identity.Identity, author *feeds.Author) *feeds.Item { 283 + return &feeds.Item{ 284 + Title: fmt.Sprintf("%s created pull request '%s' in @%s/%s", author.Name, pull.Title, owner.Handle, pull.Repo.Name), 285 + Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/pulls/%d", s.config.Core.AppviewHost, owner.Handle, pull.Repo.Name, pull.PullId), Type: "text/html", Rel: "alternate"}, 286 + Created: pull.Created, 287 + Author: author, 288 + } 289 + } 290 + 291 + func (s *State) createIssueItem(issue *db.Issue, owner *identity.Identity, author *feeds.Author) *feeds.Item { 292 + return &feeds.Item{ 293 + Title: fmt.Sprintf("%s created issue '%s' in @%s/%s", author.Name, issue.Title, owner.Handle, issue.Metadata.Repo.Name), 294 + Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/issues/%d", s.config.Core.AppviewHost, owner.Handle, issue.Metadata.Repo.Name, issue.IssueId), Type: "text/html", Rel: "alternate"}, 295 + Created: issue.Created, 296 + Author: author, 297 + } 298 + } 299 + 300 + func (s *State) createRepoItem(ctx context.Context, repo db.RepoEvent, author *feeds.Author) (*feeds.Item, error) { 301 + var title string 302 + if repo.Source != nil { 303 + sourceOwner, err := s.idResolver.ResolveIdent(ctx, repo.Source.Did) 304 + if err != nil { 305 + return nil, err 306 + } 307 + title = fmt.Sprintf("%s forked repository @%s/%s to '%s'", author.Name, sourceOwner.Handle, repo.Source.Name, repo.Repo.Name) 308 + } else { 309 + title = fmt.Sprintf("%s created repository '%s'", author.Name, repo.Repo.Name) 310 + } 311 + 312 + return &feeds.Item{ 313 + Title: title, 314 + Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s", s.config.Core.AppviewHost, author.Name[1:], repo.Repo.Name), Type: "text/html", Rel: "alternate"}, // Remove @ prefix 315 + Created: repo.Repo.Created, 316 + Author: author, 317 + }, nil 318 } 319 320 func (s *State) UpdateProfileBio(w http.ResponseWriter, r *http.Request) {