Monorepo for Tangled tangled.org

appview/timeline: basic timeline view

It's pretty rudimentary. All repos and follows (for now) are
consolidated into a []TimelineEvent and sorted by time.

anirudh.fi 63cc13ae e26055b7

verified
Changed files
+153 -4
appview
+34
appview/db/follow.go
··· 46 46 _, err := d.db.Exec(`delete from follows where user_did = ? and subject_did = ?`, userDid, subjectDid) 47 47 return err 48 48 } 49 + 50 + func (d *DB) GetAllFollows() ([]Follow, error) { 51 + var follows []Follow 52 + 53 + rows, err := d.db.Query(`select user_did, subject_did, followed_at, at_uri from follows`) 54 + if err != nil { 55 + return nil, err 56 + } 57 + defer rows.Close() 58 + 59 + for rows.Next() { 60 + var follow Follow 61 + var followedAt string 62 + if err := rows.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.RKey); err != nil { 63 + return nil, err 64 + } 65 + 66 + followedAtTime, err := time.Parse(time.RFC3339, followedAt) 67 + if err != nil { 68 + log.Println("unable to determine followed at time") 69 + follow.FollowedAt = nil 70 + } else { 71 + follow.FollowedAt = &followedAtTime 72 + } 73 + 74 + follows = append(follows, follow) 75 + } 76 + 77 + if err := rows.Err(); err != nil { 78 + return nil, err 79 + } 80 + 81 + return follows, nil 82 + }
+24
appview/db/repos.go
··· 13 13 Rkey string 14 14 } 15 15 16 + func (d *DB) GetAllRepos() ([]Repo, error) { 17 + var repos []Repo 18 + 19 + rows, err := d.db.Query(`select did, name, knot, created from repos`) 20 + if err != nil { 21 + return nil, err 22 + } 23 + defer rows.Close() 24 + 25 + for rows.Next() { 26 + repo, err := scanRepo(rows) 27 + if err != nil { 28 + return nil, err 29 + } 30 + repos = append(repos, *repo) 31 + } 32 + 33 + if err := rows.Err(); err != nil { 34 + return nil, err 35 + } 36 + 37 + return repos, nil 38 + } 39 + 16 40 func (d *DB) GetAllReposByDid(did string) ([]Repo, error) { 17 41 var repos []Repo 18 42
+48
appview/db/timeline.go
··· 1 + package db 2 + 3 + import ( 4 + "sort" 5 + "time" 6 + ) 7 + 8 + type TimelineEvent struct { 9 + *Repo 10 + *Follow 11 + EventAt *time.Time 12 + } 13 + 14 + func (d *DB) MakeTimeline() ([]TimelineEvent, error) { 15 + var events []TimelineEvent 16 + 17 + repos, err := d.GetAllRepos() 18 + if err != nil { 19 + return nil, err 20 + } 21 + 22 + follows, err := d.GetAllFollows() 23 + if err != nil { 24 + return nil, err 25 + } 26 + 27 + for _, repo := range repos { 28 + events = append(events, TimelineEvent{ 29 + Repo: &repo, 30 + Follow: nil, 31 + EventAt: repo.Created, 32 + }) 33 + } 34 + 35 + for _, follow := range follows { 36 + events = append(events, TimelineEvent{ 37 + Repo: nil, 38 + Follow: &follow, 39 + EventAt: follow.FollowedAt, 40 + }) 41 + } 42 + 43 + sort.Slice(events, func(i, j int) bool { 44 + return events[i].EventAt.After(*events[j].EventAt) 45 + }) 46 + 47 + return events, nil 48 + }
+1
appview/pages/pages.go
··· 174 174 175 175 type TimelineParams struct { 176 176 LoggedInUser *auth.User 177 + Timeline []db.TimelineEvent 177 178 } 178 179 179 180 func (p *Pages) Timeline(w io.Writer, params TimelineParams) error {
+37 -4
appview/pages/templates/timeline.html
··· 1 - {{define "title"}}timeline{{end}} 1 + {{ define "title" }}timeline{{ end }} 2 + 3 + {{ define "content" }} 4 + <h1>Timeline</h1> 5 + 6 + {{ range .Timeline }} 7 + {{ if .Repo }} 8 + <div class="border border-black p-4 m-2 bg-white w-1/2"> 9 + <div class="flex items-center"> 10 + <div class="text-sm text-gray-600"> 11 + {{ .Repo.Did }} created 12 + </div> 13 + <div class="px-3">{{ .Repo.Name }}</div> 14 + </div> 15 + 16 + <time class="text-sm text-gray-700" 17 + >{{ .Repo.Created | timeFmt }}</time 18 + > 19 + </div> 20 + {{ else if .Follow }} 21 + <div class="border border-black p-4 m-2 bg-white w-1/2"> 22 + <div class="flex items-center"> 23 + <div class="text-sm text-gray-600"> 24 + {{ .Follow.UserDid }} followed 25 + </div> 26 + <div class="text-sm text-gray-800"> 27 + {{ .Follow.SubjectDid }} 28 + </div> 29 + </div> 2 30 3 - {{define "content"}} 4 - <h1>timeline</h1> 5 - {{end}} 31 + <time class="text-sm text-gray-700" 32 + >{{ .Follow.FollowedAt | timeFmt }}</time 33 + > 34 + </div> 35 + {{ end }} 36 + {{ end }} 37 + 38 + {{ end }}
+9
appview/state/state.go
··· 157 157 158 158 func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { 159 159 user := s.auth.GetUser(r) 160 + 161 + timeline, err := s.db.MakeTimeline() 162 + if err != nil { 163 + log.Println(err) 164 + s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.") 165 + } 166 + 160 167 s.pages.Timeline(w, pages.TimelineParams{ 161 168 LoggedInUser: user, 169 + Timeline: timeline, 162 170 }) 171 + 163 172 return 164 173 } 165 174