···32 // like comment counts, parent repo etc.
33 Comments []IssueComment
34 Labels models.LabelState
35- Repo *Repo
36}
3738func (i *Issue) AtUri() syntax.ATURI {
···376 return nil, fmt.Errorf("failed to build repo mappings: %w", err)
377 }
378379- repoMap := make(map[string]*Repo)
380 for i := range repos {
381 repoMap[string(repos[i].RepoAt())] = &repos[i]
382 }
···658 return err
659}
660661-type IssueCount struct {
662- Open int
663- Closed int
664-}
665-666-func GetIssueCount(e Execer, repoAt syntax.ATURI) (IssueCount, error) {
667 row := e.QueryRow(`
668 select
669 count(case when open = 1 then 1 end) as open_count,
···673 repoAt,
674 )
675676- var count IssueCount
677 if err := row.Scan(&count.Open, &count.Closed); err != nil {
678- return IssueCount{0, 0}, err
679 }
680681 return count, nil
···32 // like comment counts, parent repo etc.
33 Comments []IssueComment
34 Labels models.LabelState
35+ Repo *models.Repo
36}
3738func (i *Issue) AtUri() syntax.ATURI {
···376 return nil, fmt.Errorf("failed to build repo mappings: %w", err)
377 }
378379+ repoMap := make(map[string]*models.Repo)
380 for i := range repos {
381 repoMap[string(repos[i].RepoAt())] = &repos[i]
382 }
···658 return err
659}
660661+func GetIssueCount(e Execer, repoAt syntax.ATURI) (models.IssueCount, error) {
00000662 row := e.QueryRow(`
663 select
664 count(case when open = 1 then 1 end) as open_count,
···668 repoAt,
669 )
670671+ var count models.IssueCount
672 if err := row.Scan(&count.Open, &count.Closed); err != nil {
673+ return models.IssueCount{0, 0}, err
674 }
675676 return count, nil
+4-3
appview/db/profile.go
···1112 "github.com/bluesky-social/indigo/atproto/syntax"
13 "tangled.org/core/api/tangled"
014)
1516type RepoEvent struct {
17- Repo *Repo
18- Source *Repo
19}
2021type ProfileTimeline struct {
···162163 for _, repo := range repos {
164 // TODO: get this in the original query; requires COALESCE because nullable
165- var sourceRepo *Repo
166 if repo.Source != "" {
167 sourceRepo, err = GetRepoByAtUri(e, repo.Source)
168 if err != nil {
···1112 "github.com/bluesky-social/indigo/atproto/syntax"
13 "tangled.org/core/api/tangled"
14+ "tangled.org/core/appview/models"
15)
1617type RepoEvent struct {
18+ Repo *models.Repo
19+ Source *models.Repo
20}
2122type ProfileTimeline struct {
···163164 for _, repo := range repos {
165 // TODO: get this in the original query; requires COALESCE because nullable
166+ var sourceRepo *models.Repo
167 if repo.Source != "" {
168 sourceRepo, err = GetRepoByAtUri(e, repo.Source)
169 if err != nil {
+8-14
appview/db/pulls.go
···1112 "github.com/bluesky-social/indigo/atproto/syntax"
13 "tangled.org/core/api/tangled"
014 "tangled.org/core/patchutil"
15 "tangled.org/core/types"
16)
···79 PullSource *PullSource
8081 // optionally, populate this when querying for reverse mappings
82- Repo *Repo
83}
8485func (p Pull) AsRecord() tangled.RepoPull {
···109 RepoAt *syntax.ATURI
110111 // optionally populate this for reverse mappings
112- Repo *Repo
113}
114115func (p PullSource) AsRecord() tangled.RepoPull_Source {
···723 return nil, err
724 }
725726- var pullSourceRepo *Repo
727 if pull.PullSource != nil {
728 if pull.PullSource.RepoAt != nil {
729 pullSourceRepo, err = GetRepoByAtUri(e, pull.PullSource.RepoAt.String())
···776777 for rows.Next() {
778 var pull Pull
779- var repo Repo
780 var pullCreatedAt, repoCreatedAt string
781 err := rows.Scan(
782 &pull.OwnerDid,
···931 return err
932}
933934-type PullCount struct {
935- Open int
936- Merged int
937- Closed int
938- Deleted int
939-}
940-941-func GetPullCount(e Execer, repoAt syntax.ATURI) (PullCount, error) {
942 row := e.QueryRow(`
943 select
944 count(case when state = ? then 1 end) as open_count,
···954 repoAt,
955 )
956957- var count PullCount
958 if err := row.Scan(&count.Open, &count.Merged, &count.Closed, &count.Deleted); err != nil {
959- return PullCount{0, 0, 0, 0}, err
960 }
961962 return count, nil
···1112 "github.com/bluesky-social/indigo/atproto/syntax"
13 "tangled.org/core/api/tangled"
14+ "tangled.org/core/appview/models"
15 "tangled.org/core/patchutil"
16 "tangled.org/core/types"
17)
···80 PullSource *PullSource
8182 // optionally, populate this when querying for reverse mappings
83+ Repo *models.Repo
84}
8586func (p Pull) AsRecord() tangled.RepoPull {
···110 RepoAt *syntax.ATURI
111112 // optionally populate this for reverse mappings
113+ Repo *models.Repo
114}
115116func (p PullSource) AsRecord() tangled.RepoPull_Source {
···724 return nil, err
725 }
726727+ var pullSourceRepo *models.Repo
728 if pull.PullSource != nil {
729 if pull.PullSource.RepoAt != nil {
730 pullSourceRepo, err = GetRepoByAtUri(e, pull.PullSource.RepoAt.String())
···777778 for rows.Next() {
779 var pull Pull
780+ var repo models.Repo
781 var pullCreatedAt, repoCreatedAt string
782 err := rows.Scan(
783 &pull.OwnerDid,
···932 return err
933}
934935+func GetPullCount(e Execer, repoAt syntax.ATURI) (models.PullCount, error) {
0000000936 row := e.QueryRow(`
937 select
938 count(case when state = ? then 1 end) as open_count,
···948 repoAt,
949 )
950951+ var count models.PullCount
952 if err := row.Scan(&count.Open, &count.Merged, &count.Closed, &count.Deleted); err != nil {
953+ return models.PullCount{Open: 0, Merged: 0, Closed: 0, Deleted: 0}, err
954 }
955956 return count, nil
+20-86
appview/db/repos.go
···10 "time"
1112 "github.com/bluesky-social/indigo/atproto/syntax"
13- securejoin "github.com/cyphar/filepath-securejoin"
14- "tangled.org/core/api/tangled"
15)
1617-type Repo struct {
18- Did string
19- Name string
20- Knot string
21- Rkey string
22- Created time.Time
23- Description string
24- Spindle string
25- Labels []string
26-27- // optionally, populate this when querying for reverse mappings
28- RepoStats *RepoStats
29-30- // optional
31- Source string
32-}
33-34-func (r *Repo) AsRecord() tangled.Repo {
35- var source, spindle, description *string
36-37- if r.Source != "" {
38- source = &r.Source
39- }
40-41- if r.Spindle != "" {
42- spindle = &r.Spindle
43- }
44-45- if r.Description != "" {
46- description = &r.Description
47- }
48-49- return tangled.Repo{
50- Knot: r.Knot,
51- Name: r.Name,
52- Description: description,
53- CreatedAt: r.Created.Format(time.RFC3339),
54- Source: source,
55- Spindle: spindle,
56- Labels: r.Labels,
57- }
58-}
59-60-func (r Repo) RepoAt() syntax.ATURI {
61- return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", r.Did, tangled.RepoNSID, r.Rkey))
62-}
63-64-func (r Repo) DidSlashRepo() string {
65- p, _ := securejoin.SecureJoin(r.Did, r.Name)
66- return p
67-}
68-69-func GetRepos(e Execer, limit int, filters ...filter) ([]Repo, error) {
70- repoMap := make(map[syntax.ATURI]*Repo)
7172 var conditions []string
73 var args []any
···111 }
112113 for rows.Next() {
114- var repo Repo
115 var createdAt string
116 var description, source, spindle sql.NullString
117···142 repo.Spindle = spindle.String
143 }
144145- repo.RepoStats = &RepoStats{}
146 repoMap[repo.RepoAt()] = &repo
147 }
148···320 return nil, fmt.Errorf("failed to execute pulls-count query: %w ", err)
321 }
322323- var repos []Repo
324 for _, r := range repoMap {
325 repos = append(repos, *r)
326 }
327328- slices.SortFunc(repos, func(a, b Repo) int {
329 if a.Created.After(b.Created) {
330 return -1
331 }
···336}
337338// helper to get exactly one repo
339-func GetRepo(e Execer, filters ...filter) (*Repo, error) {
340 repos, err := GetRepos(e, 0, filters...)
341 if err != nil {
342 return nil, err
···377 return count, nil
378}
379380-func GetRepoByAtUri(e Execer, atUri string) (*Repo, error) {
381- var repo Repo
382 var nullableDescription sql.NullString
383384 row := e.QueryRow(`select did, name, knot, created, rkey, description from repos where at_uri = ?`, atUri)
···399 return &repo, nil
400}
401402-func AddRepo(e Execer, repo *Repo) error {
403 _, err := e.Exec(
404 `insert into repos
405 (did, name, knot, rkey, at_uri, description, source)
···423 return nullableSource.String, nil
424}
425426-func GetForksByDid(e Execer, did string) ([]Repo, error) {
427- var repos []Repo
428429 rows, err := e.Query(
430 `select distinct r.did, r.name, r.knot, r.rkey, r.description, r.created, r.source
···442 defer rows.Close()
443444 for rows.Next() {
445- var repo Repo
446 var createdAt string
447 var nullableDescription sql.NullString
448 var nullableSource sql.NullString
···477 return repos, nil
478}
479480-func GetForkByDid(e Execer, did string, name string) (*Repo, error) {
481- var repo Repo
482 var createdAt string
483 var nullableDescription sql.NullString
484 var nullableSource sql.NullString
···525 return err
526}
527528-type RepoStats struct {
529- Language string
530- StarCount int
531- IssueCount IssueCount
532- PullCount PullCount
533-}
534-535-type RepoLabel struct {
536- Id int64
537- RepoAt syntax.ATURI
538- LabelAt syntax.ATURI
539-}
540-541-func SubscribeLabel(e Execer, rl *RepoLabel) error {
542 query := `insert or ignore into repo_labels (repo_at, label_at) values (?, ?)`
543544 _, err := e.Exec(query, rl.RepoAt.String(), rl.LabelAt.String())
···563 return err
564}
565566-func GetRepoLabels(e Execer, filters ...filter) ([]RepoLabel, error) {
567 var conditions []string
568 var args []any
569 for _, filter := range filters {
···584 }
585 defer rows.Close()
586587- var labels []RepoLabel
588 for rows.Next() {
589- var label RepoLabel
590591 err := rows.Scan(&label.Id, &label.RepoAt, &label.LabelAt)
592 if err != nil {
···10 "time"
1112 "github.com/bluesky-social/indigo/atproto/syntax"
13+ "tangled.org/core/appview/models"
014)
1516+func GetRepos(e Execer, limit int, filters ...filter) ([]models.Repo, error) {
17+ repoMap := make(map[syntax.ATURI]*models.Repo)
00000000000000000000000000000000000000000000000000001819 var conditions []string
20 var args []any
···58 }
5960 for rows.Next() {
61+ var repo models.Repo
62 var createdAt string
63 var description, source, spindle sql.NullString
64···89 repo.Spindle = spindle.String
90 }
9192+ repo.RepoStats = &models.RepoStats{}
93 repoMap[repo.RepoAt()] = &repo
94 }
95···267 return nil, fmt.Errorf("failed to execute pulls-count query: %w ", err)
268 }
269270+ var repos []models.Repo
271 for _, r := range repoMap {
272 repos = append(repos, *r)
273 }
274275+ slices.SortFunc(repos, func(a, b models.Repo) int {
276 if a.Created.After(b.Created) {
277 return -1
278 }
···283}
284285// helper to get exactly one repo
286+func GetRepo(e Execer, filters ...filter) (*models.Repo, error) {
287 repos, err := GetRepos(e, 0, filters...)
288 if err != nil {
289 return nil, err
···324 return count, nil
325}
326327+func GetRepoByAtUri(e Execer, atUri string) (*models.Repo, error) {
328+ var repo models.Repo
329 var nullableDescription sql.NullString
330331 row := e.QueryRow(`select did, name, knot, created, rkey, description from repos where at_uri = ?`, atUri)
···346 return &repo, nil
347}
348349+func AddRepo(e Execer, repo *models.Repo) error {
350 _, err := e.Exec(
351 `insert into repos
352 (did, name, knot, rkey, at_uri, description, source)
···370 return nullableSource.String, nil
371}
372373+func GetForksByDid(e Execer, did string) ([]models.Repo, error) {
374+ var repos []models.Repo
375376 rows, err := e.Query(
377 `select distinct r.did, r.name, r.knot, r.rkey, r.description, r.created, r.source
···389 defer rows.Close()
390391 for rows.Next() {
392+ var repo models.Repo
393 var createdAt string
394 var nullableDescription sql.NullString
395 var nullableSource sql.NullString
···424 return repos, nil
425}
426427+func GetForkByDid(e Execer, did string, name string) (*models.Repo, error) {
428+ var repo models.Repo
429 var createdAt string
430 var nullableDescription sql.NullString
431 var nullableSource sql.NullString
···472 return err
473}
474475+func SubscribeLabel(e Execer, rl *models.RepoLabel) error {
0000000000000476 query := `insert or ignore into repo_labels (repo_at, label_at) values (?, ?)`
477478 _, err := e.Exec(query, rl.RepoAt.String(), rl.LabelAt.String())
···497 return err
498}
499500+func GetRepoLabels(e Execer, filters ...filter) ([]models.RepoLabel, error) {
501 var conditions []string
502 var args []any
503 for _, filter := range filters {
···518 }
519 defer rows.Close()
520521+ var labels []models.RepoLabel
522 for rows.Next() {
523+ var label models.RepoLabel
524525 err := rows.Scan(&label.Id, &label.RepoAt, &label.LabelAt)
526 if err != nil {
+7-6
appview/db/star.go
···9 "time"
1011 "github.com/bluesky-social/indigo/atproto/syntax"
012)
1314type Star struct {
···18 Rkey string
1920 // optionally, populate this when querying for reverse mappings
21- Repo *Repo
22}
2324func (star *Star) ResolveRepo(e Execer) error {
···284285 for rows.Next() {
286 var star Star
287- var repo Repo
288 var starCreatedAt, repoCreatedAt string
289290 if err := rows.Scan(
···322}
323324// GetTopStarredReposLastWeek returns the top 8 most starred repositories from the last week
325-func GetTopStarredReposLastWeek(e Execer) ([]Repo, error) {
326 // first, get the top repo URIs by star count from the last week
327 query := `
328 with recent_starred_repos as (
···366 }
367368 if len(repoUris) == 0 {
369- return []Repo{}, nil
370 }
371372 // get full repo data
···376 }
377378 // sort repos by the original trending order
379- repoMap := make(map[string]Repo)
380 for _, repo := range repos {
381 repoMap[repo.RepoAt().String()] = repo
382 }
383384- orderedRepos := make([]Repo, 0, len(repoUris))
385 for _, uri := range repoUris {
386 if repo, exists := repoMap[uri]; exists {
387 orderedRepos = append(orderedRepos, repo)
···9 "time"
1011 "github.com/bluesky-social/indigo/atproto/syntax"
12+ "tangled.org/core/appview/models"
13)
1415type Star struct {
···19 Rkey string
2021 // optionally, populate this when querying for reverse mappings
22+ Repo *models.Repo
23}
2425func (star *Star) ResolveRepo(e Execer) error {
···285286 for rows.Next() {
287 var star Star
288+ var repo models.Repo
289 var starCreatedAt, repoCreatedAt string
290291 if err := rows.Scan(
···323}
324325// GetTopStarredReposLastWeek returns the top 8 most starred repositories from the last week
326+func GetTopStarredReposLastWeek(e Execer) ([]models.Repo, error) {
327 // first, get the top repo URIs by star count from the last week
328 query := `
329 with recent_starred_repos as (
···367 }
368369 if len(repoUris) == 0 {
370+ return []models.Repo{}, nil
371 }
372373 // get full repo data
···377 }
378379 // sort repos by the original trending order
380+ repoMap := make(map[string]models.Repo)
381 for _, repo := range repos {
382 repoMap[repo.RepoAt().String()] = repo
383 }
384385+ orderedRepos := make([]models.Repo, 0, len(repoUris))
386 for _, uri := range repoUris {
387 if repo, exists := repoMap[uri]; exists {
388 orderedRepos = append(orderedRepos, repo)
+8-8
appview/db/timeline.go
···9)
1011type TimelineEvent struct {
12- *Repo
13 *models.Follow
14 *Star
1516 EventAt time.Time
1718 // optional: populate only if Repo is a fork
19- Source *Repo
2021 // optional: populate only if event is Follow
22 *Profile
···64 return events, nil
65}
6667-func fetchStarStatuses(e Execer, loggedInUserDid string, repos []Repo) (map[string]bool, error) {
68 if loggedInUserDid == "" {
69 return nil, nil
70 }
···77 return GetStarStatuses(e, loggedInUserDid, repoAts)
78}
7980-func getRepoStarInfo(repo *Repo, starStatuses map[string]bool) (bool, int64) {
81 var isStarred bool
82 if starStatuses != nil {
83 isStarred = starStatuses[repo.RepoAt().String()]
···105 }
106 }
107108- var origRepos []Repo
109 if args != nil {
110 origRepos, err = GetRepos(e, 0, FilterIn("at_uri", args))
111 }
···113 return nil, err
114 }
115116- uriToRepo := make(map[string]Repo)
117 for _, r := range origRepos {
118 uriToRepo[r.RepoAt().String()] = r
119 }
···125126 var events []TimelineEvent
127 for _, r := range repos {
128- var source *Repo
129 if r.Source != "" {
130 if origRepo, ok := uriToRepo[r.Source]; ok {
131 source = &origRepo
···162 }
163 stars = stars[:n]
164165- var repos []Repo
166 for _, s := range stars {
167 repos = append(repos, *s.Repo)
168 }
···9)
1011type TimelineEvent struct {
12+ *models.Repo
13 *models.Follow
14 *Star
1516 EventAt time.Time
1718 // optional: populate only if Repo is a fork
19+ Source *models.Repo
2021 // optional: populate only if event is Follow
22 *Profile
···64 return events, nil
65}
6667+func fetchStarStatuses(e Execer, loggedInUserDid string, repos []models.Repo) (map[string]bool, error) {
68 if loggedInUserDid == "" {
69 return nil, nil
70 }
···77 return GetStarStatuses(e, loggedInUserDid, repoAts)
78}
7980+func getRepoStarInfo(repo *models.Repo, starStatuses map[string]bool) (bool, int64) {
81 var isStarred bool
82 if starStatuses != nil {
83 isStarred = starStatuses[repo.RepoAt().String()]
···105 }
106 }
107108+ var origRepos []models.Repo
109 if args != nil {
110 origRepos, err = GetRepos(e, 0, FilterIn("at_uri", args))
111 }
···113 return nil, err
114 }
115116+ uriToRepo := make(map[string]models.Repo)
117 for _, r := range origRepos {
118 uriToRepo[r.RepoAt().String()] = r
119 }
···125126 var events []TimelineEvent
127 for _, r := range repos {
128+ var source *models.Repo
129 if r.Source != "" {
130 if origRepo, ok := uriToRepo[r.Source]; ok {
131 source = &origRepo
···162 }
163 stars = stars[:n]
164165+ var repos []models.Repo
166 for _, s := range stars {
167 repos = append(repos, *s.Repo)
168 }
+2-1
appview/knots/knots.go
···13 "tangled.org/core/appview/config"
14 "tangled.org/core/appview/db"
15 "tangled.org/core/appview/middleware"
016 "tangled.org/core/appview/oauth"
17 "tangled.org/core/appview/pages"
18 "tangled.org/core/appview/serververify"
···119 }
120121 // organize repos by did
122- repoMap := make(map[string][]db.Repo)
123 for _, r := range repos {
124 repoMap[r.Did] = append(repoMap[r.Did], r)
125 }
···13 "tangled.org/core/appview/config"
14 "tangled.org/core/appview/db"
15 "tangled.org/core/appview/middleware"
16+ "tangled.org/core/appview/models"
17 "tangled.org/core/appview/oauth"
18 "tangled.org/core/appview/pages"
19 "tangled.org/core/appview/serververify"
···120 }
121122 // organize repos by did
123+ repoMap := make(map[string][]models.Repo)
124 for _, r := range repos {
125 repoMap[r.Did] = append(repoMap[r.Did], r)
126 }
···13 "tangled.org/core/appview/config"
14 "tangled.org/core/appview/db"
15 "tangled.org/core/appview/middleware"
016 "tangled.org/core/appview/oauth"
17 "tangled.org/core/appview/pages"
18 "tangled.org/core/appview/serververify"
···115 }
116117 // organize repos by did
118- repoMap := make(map[string][]db.Repo)
119 for _, r := range repos {
120 repoMap[r.Did] = append(repoMap[r.Did], r)
121 }
···13 "tangled.org/core/appview/config"
14 "tangled.org/core/appview/db"
15 "tangled.org/core/appview/middleware"
16+ "tangled.org/core/appview/models"
17 "tangled.org/core/appview/oauth"
18 "tangled.org/core/appview/pages"
19 "tangled.org/core/appview/serververify"
···116 }
117118 // organize repos by did
119+ repoMap := make(map[string][]models.Repo)
120 for _, r := range repos {
121 repoMap[r.Did] = append(repoMap[r.Did], r)
122 }
+4-4
appview/state/git_http.go
···89 "github.com/bluesky-social/indigo/atproto/identity"
10 "github.com/go-chi/chi/v5"
11- "tangled.org/core/appview/db"
12)
1314func (s *State) InfoRefs(w http.ResponseWriter, r *http.Request) {
15 user := r.Context().Value("resolvedId").(identity.Identity)
16- repo := r.Context().Value("repo").(*db.Repo)
1718 scheme := "https"
19 if s.config.Core.Dev {
···31 http.Error(w, "failed to resolve user", http.StatusInternalServerError)
32 return
33 }
34- repo := r.Context().Value("repo").(*db.Repo)
3536 scheme := "https"
37 if s.config.Core.Dev {
···48 http.Error(w, "failed to resolve user", http.StatusInternalServerError)
49 return
50 }
51- repo := r.Context().Value("repo").(*db.Repo)
5253 scheme := "https"
54 if s.config.Core.Dev {
···89 "github.com/bluesky-social/indigo/atproto/identity"
10 "github.com/go-chi/chi/v5"
11+ "tangled.org/core/appview/models"
12)
1314func (s *State) InfoRefs(w http.ResponseWriter, r *http.Request) {
15 user := r.Context().Value("resolvedId").(identity.Identity)
16+ repo := r.Context().Value("repo").(*models.Repo)
1718 scheme := "https"
19 if s.config.Core.Dev {
···31 http.Error(w, "failed to resolve user", http.StatusInternalServerError)
32 return
33 }
34+ repo := r.Context().Value("repo").(*models.Repo)
3536 scheme := "https"
37 if s.config.Core.Dev {
···48 http.Error(w, "failed to resolve user", http.StatusInternalServerError)
49 return
50 }
51+ repo := r.Context().Value("repo").(*models.Repo)
5253 scheme := "https"
54 if s.config.Core.Dev {
+2-2
appview/state/profile.go
···131 }
132133 // filter out ones that are pinned
134- pinnedRepos := []db.Repo{}
135 for i, r := range repos {
136 // if this is a pinned repo, add it
137 if slices.Contains(profile.Profile.PinnedRepos[:], r.RepoAt()) {
···149 l.Error("failed to fetch collaborating repos", "err", err)
150 }
151152- pinnedCollaboratingRepos := []db.Repo{}
153 for _, r := range collaboratingRepos {
154 // if this is a pinned repo, add it
155 if slices.Contains(profile.Profile.PinnedRepos[:], r.RepoAt()) {
···131 }
132133 // filter out ones that are pinned
134+ pinnedRepos := []models.Repo{}
135 for i, r := range repos {
136 // if this is a pinned repo, add it
137 if slices.Contains(profile.Profile.PinnedRepos[:], r.RepoAt()) {
···149 l.Error("failed to fetch collaborating repos", "err", err)
150 }
151152+ pinnedCollaboratingRepos := []models.Repo{}
153 for _, r := range collaboratingRepos {
154 // if this is a pinned repo, add it
155 if slices.Contains(profile.Profile.PinnedRepos[:], r.RepoAt()) {