A vibe coded tangled fork which supports pijul.
1package repoinfo
2
3import (
4 "fmt"
5 "path"
6 "slices"
7
8 "github.com/bluesky-social/indigo/atproto/syntax"
9 "tangled.org/core/api/tangled"
10 "tangled.org/core/appview/models"
11 "tangled.org/core/appview/state/userutil"
12)
13
14func (r RepoInfo) owner() string {
15 if r.OwnerHandle != "" {
16 return r.OwnerHandle
17 } else {
18 return r.OwnerDid
19 }
20}
21
22func (r RepoInfo) FullName() string {
23 return path.Join(r.owner(), r.Name)
24}
25
26func (r RepoInfo) ownerWithoutAt() string {
27 if r.OwnerHandle != "" {
28 return r.OwnerHandle
29 } else {
30 return userutil.FlattenDid(r.OwnerDid)
31 }
32}
33
34func (r RepoInfo) FullNameWithoutAt() string {
35 return path.Join(r.ownerWithoutAt(), r.Name)
36}
37
38func (r RepoInfo) GetTabs() [][]string {
39 tabs := [][]string{
40 {"overview", "/", "square-chart-gantt"},
41 }
42
43 if r.IsPijul() {
44 tabs = append(tabs, []string{"changes", "/changes", "logs"})
45 tabs = append(tabs, []string{"discussions", "/discussions", "message-square"})
46 } else {
47 tabs = append(tabs, []string{"issues", "/issues", "circle-dot"})
48 tabs = append(tabs, []string{"pulls", "/pulls", "git-pull-request"})
49 }
50
51 tabs = append(tabs, []string{"pipelines", "/pipelines", "layers-2"})
52
53 if r.Roles.SettingsAllowed() {
54 tabs = append(tabs, []string{"settings", "/settings", "cog"})
55 }
56
57 return tabs
58}
59
60func (r RepoInfo) RepoAt() syntax.ATURI {
61 return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", r.OwnerDid, tangled.RepoNSID, r.Rkey))
62}
63
64type RepoInfo struct {
65 Name string
66 Rkey string
67 OwnerDid string
68 OwnerHandle string
69 Description string
70 Website string
71 Topics []string
72 Knot string
73 Spindle string
74 Vcs string // "git" or "pijul"
75 IsStarred bool
76 Stats models.RepoStats
77 Roles RolesInRepo
78 Source *models.Repo
79 Ref string
80 CurrentDir string
81}
82
83func (r RepoInfo) IsGit() bool {
84 return r.Vcs == "" || r.Vcs == "git"
85}
86
87func (r RepoInfo) IsPijul() bool {
88 return r.Vcs == "pijul"
89}
90
91// each tab on a repo could have some metadata:
92//
93// issues -> number of open issues etc.
94// settings -> a warning icon to setup branch protection? idk
95//
96// we gather these bits of info here, because go templates
97// are difficult to program in
98func (r RepoInfo) TabMetadata() map[string]any {
99 meta := make(map[string]any)
100
101 if r.IsPijul() {
102 // Pijul repos use discussions
103 meta["discussions"] = r.Stats.DiscussionCount.Open
104 } else {
105 // Git repos use separate issues and pulls
106 meta["issues"] = r.Stats.IssueCount.Open
107 meta["pulls"] = r.Stats.PullCount.Open
108 }
109
110 return meta
111}
112
113type RolesInRepo struct {
114 Roles []string
115}
116
117func (r RolesInRepo) SettingsAllowed() bool {
118 return slices.Contains(r.Roles, "repo:settings")
119}
120
121func (r RolesInRepo) CollaboratorInviteAllowed() bool {
122 return slices.Contains(r.Roles, "repo:invite")
123}
124
125func (r RolesInRepo) RepoDeleteAllowed() bool {
126 return slices.Contains(r.Roles, "repo:delete")
127}
128
129func (r RolesInRepo) IsOwner() bool {
130 return slices.Contains(r.Roles, "repo:owner")
131}
132
133func (r RolesInRepo) IsCollaborator() bool {
134 return slices.Contains(r.Roles, "repo:collaborator")
135}
136
137func (r RolesInRepo) IsPushAllowed() bool {
138 return slices.Contains(r.Roles, "repo:push")
139}
140
141// CanManageRepo returns true if the user has any role that grants management
142// access (owner, collaborator, or push). Used for actions like closing/merging
143// discussions, removing patches, etc.
144func (r RolesInRepo) CanManageRepo() bool {
145 return r.IsOwner() || r.IsCollaborator() || r.IsPushAllowed()
146}