forked from tangled.org/core
Monorepo for Tangled

add collaborating-on

Changed files
+124 -24
appview
db
pages
templates
state
knotserver
git
types
appview/appview

This is a binary file and will not be displayed.

+6
appview/db/db.go
··· 49 49 created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 50 50 unique(did, name, knot) 51 51 ); 52 + create table if not exists collaborators ( 53 + id integer primary key autoincrement, 54 + did text not null, 55 + repo integer not null, 56 + foreign key (repo) references repos(id) on delete cascade 57 + ); 52 58 create table if not exists follows ( 53 59 user_did text not null, 54 60 subject_did text not null,
+57 -7
appview/db/repos.go
··· 1 1 package db 2 2 3 - import "time" 3 + import ( 4 + "database/sql" 5 + "time" 6 + ) 4 7 5 8 type Repo struct { 6 9 Did string ··· 19 22 defer rows.Close() 20 23 21 24 for rows.Next() { 22 - var repo Repo 23 - var createdAt string 24 - if err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt); err != nil { 25 + repo, err := scanRepo(rows) 26 + if err != nil { 25 27 return nil, err 26 28 } 27 - createdAtTime, _ := time.Parse(time.RFC3339, createdAt) 28 - repo.Created = &createdAtTime 29 - repos = append(repos, repo) 29 + repos = append(repos, *repo) 30 30 } 31 31 32 32 if err := rows.Err(); err != nil { ··· 60 60 _, err := d.db.Exec(`delete from repos where did = ? and name = ? and knot = ?`, did, name, knot) 61 61 return err 62 62 } 63 + 64 + func (d *DB) AddCollaborator(collaborator, repoOwnerDid, repoName, repoKnot string) error { 65 + _, err := d.db.Exec( 66 + `insert into collaborators (did, repo) 67 + values (?, (select id from repos where did = ? and name = ? and knot = ?));`, 68 + collaborator, repoOwnerDid, repoName, repoKnot) 69 + return err 70 + } 71 + 72 + func (d *DB) CollaboratingIn(collaborator string) ([]Repo, error) { 73 + var repos []Repo 74 + 75 + rows, err := d.db.Query(`select r.* from repos r join collaborators c on r.id = c.repo where c.did = ?;`, collaborator) 76 + if err != nil { 77 + return nil, err 78 + } 79 + defer rows.Close() 80 + 81 + for rows.Next() { 82 + repo, err := scanRepo(rows) 83 + if err != nil { 84 + return nil, err 85 + } 86 + repos = append(repos, *repo) 87 + } 88 + 89 + if err := rows.Err(); err != nil { 90 + return nil, err 91 + } 92 + 93 + return repos, nil 94 + } 95 + 96 + func scanRepo(rows *sql.Rows) (*Repo, error) { 97 + var repo Repo 98 + var createdAt string 99 + if err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt); err != nil { 100 + return nil, err 101 + } 102 + 103 + createdAtTime, err := time.Parse(time.RFC3339, createdAt) 104 + if err != nil { 105 + now := time.Now() 106 + repo.Created = &now 107 + } 108 + 109 + repo.Created = &createdAtTime 110 + 111 + return &repo, nil 112 + }
+11 -4
appview/pages/pages.go
··· 29 29 "split": func(s string) []string { 30 30 return strings.Split(s, "\n") 31 31 }, 32 + "splitOn": func(s, sep string) []string { 33 + return strings.Split(s, sep) 34 + }, 32 35 "add": func(a, b int) int { 33 36 return a + b 34 37 }, ··· 89 92 } 90 93 91 94 return strings.Join(paragraphs, "\n\n") 95 + }, 96 + "sequence": func(n int) []struct{} { 97 + return make([]struct{}, n) 92 98 }, 93 99 } 94 100 } ··· 200 206 } 201 207 202 208 type ProfilePageParams struct { 203 - LoggedInUser *auth.User 204 - UserDid string 205 - UserHandle string 206 - Repos []db.Repo 209 + LoggedInUser *auth.User 210 + UserDid string 211 + UserHandle string 212 + Repos []db.Repo 213 + CollaboratingRepos []db.Repo 207 214 } 208 215 209 216 func (p *Pages) ProfilePage(w io.Writer, params ProfilePageParams) error {
+5 -4
appview/pages/templates/repo/commit.html
··· 102 102 {{ else }} 103 103 <pre class="overflow-auto"> 104 104 {{- range .TextFragments -}} 105 - <div class="bg-gray-100 text-gray-500">{{ nl2br .Header }}</div> 105 + <div class="bg-gray-100 text-gray-500 select-none">{{ .Comment }}</div> 106 + 106 107 {{- range .Lines -}} 107 108 {{- if eq .Op.String "+" -}} 108 - <div class="bg-green-100 text-green-700">{{ .String }}</div> 109 + <div class="bg-green-100 text-green-700"><span class="select-none">{{ .Op.String }}</span><span>{{ .Line }}</span></div> 109 110 {{- end -}} 110 111 111 112 {{- if eq .Op.String "-" -}} 112 - <div class="bg-red-100 text-red-700">{{ .String }}</div> 113 + <div class="bg-red-100 text-red-700"><span class="select-none">{{ .Op.String }}</span><span>{{ .Line }}</span></div> 113 114 {{- end -}} 114 115 115 116 {{- if eq .Op.String " " -}} 116 - <div class="text-gray-500">{{ .String }}</div> 117 + <div class="text-gray-500"><span class="select-none">{{ .Op.String }}</span><span>{{ .Line }}</span></div> 117 118 {{- end -}} 118 119 119 120 {{- end -}}
+25 -1
appview/pages/templates/user/profile.html
··· 2 2 3 3 {{ define "content" }} 4 4 <h1>{{ didOrHandle .UserDid .UserHandle }}</h1> 5 - <div id="my-repos" class="grid grid-cols-1 md:grid-cols-2 gap-4"> 5 + <p class="text-xs font-bold py-2">REPOS</p> 6 + <div id="repos" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> 6 7 {{ range .Repos }} 7 8 <div 8 9 id="repo-card" ··· 22 23 </div> 23 24 {{ else }} 24 25 <p>This user does not have any repos yet.</p> 26 + {{ end }} 27 + </div> 28 + <p class="text-xs font-bold py-2">COLLABORATING ON</p> 29 + <div id="collaborating" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> 30 + {{ range .CollaboratingRepos }} 31 + <div 32 + id="repo-card" 33 + class="border border-black p-4 shadow-sm bg-white" 34 + > 35 + <div id="repo-card-name" class="font-medium"> 36 + <a href="/@{{ or $.UserHandle $.UserDid }}/{{ .Name }}"> 37 + @{{ or $.UserHandle $.UserDid }}/{{ .Name }} 38 + </a> 39 + </div> 40 + <div 41 + id="repo-knot-name" 42 + class="text-gray-600 text-sm font-mono" 43 + > 44 + {{ .Knot }} 45 + </div> 46 + </div> 47 + {{ else }} 48 + <p>This user is not collaborating.</p> 25 49 {{ end }} 26 50 </div> 27 51 {{ end }}
+6
appview/state/repo.go
··· 393 393 return 394 394 } 395 395 396 + err = s.db.AddCollaborator(collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot) 397 + if err != nil { 398 + w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 399 + return 400 + } 401 + 396 402 w.Write([]byte(fmt.Sprint("added collaborator: ", collaboratorIdent.Handle.String()))) 397 403 398 404 }
+10 -4
appview/state/state.go
··· 580 580 log.Printf("getting repos for %s: %s", ident.DID.String(), err) 581 581 } 582 582 583 + collaboratingRepos, err := s.db.CollaboratingIn(ident.DID.String()) 584 + if err != nil { 585 + log.Printf("getting collaborating repos for %s: %s", ident.DID.String(), err) 586 + } 587 + 583 588 s.pages.ProfilePage(w, pages.ProfilePageParams{ 584 - LoggedInUser: s.auth.GetUser(r), 585 - UserDid: ident.DID.String(), 586 - UserHandle: ident.Handle.String(), 587 - Repos: repos, 589 + LoggedInUser: s.auth.GetUser(r), 590 + UserDid: ident.DID.String(), 591 + UserHandle: ident.Handle.String(), 592 + Repos: repos, 593 + CollaboratingRepos: collaboratingRepos, 588 594 }) 589 595 } 590 596
+2 -2
knotserver/git/diff.go
··· 66 66 67 67 for _, tf := range d.TextFragments { 68 68 ndiff.TextFragments = append(ndiff.TextFragments, types.TextFragment{ 69 - Header: tf.Header(), 70 - Lines: tf.Lines, 69 + Comment: tf.Comment, 70 + Lines: tf.Lines, 71 71 }) 72 72 for _, l := range tf.Lines { 73 73 switch l.Op {
+2 -2
types/diff.go
··· 6 6 ) 7 7 8 8 type TextFragment struct { 9 - Header string `json:"header"` 10 - Lines []gitdiff.Line `json:"lines"` 9 + Comment string `json:"comment"` 10 + Lines []gitdiff.Line `json:"lines"` 11 11 } 12 12 13 13 type Diff struct {