+5
-5
appview/db/follow.go
+5
-5
appview/db/follow.go
···
8
8
type Follow struct {
9
9
UserDid string
10
10
SubjectDid string
11
-
FollowedAt *time.Time
11
+
FollowedAt time.Time
12
12
RKey string
13
13
}
14
14
···
33
33
followedAtTime, err := time.Parse(time.RFC3339, followedAt)
34
34
if err != nil {
35
35
log.Println("unable to determine followed at time")
36
-
follow.FollowedAt = nil
36
+
follow.FollowedAt = time.Now()
37
37
} else {
38
-
follow.FollowedAt = &followedAtTime
38
+
follow.FollowedAt = followedAtTime
39
39
}
40
40
41
41
return &follow, nil
···
110
110
followedAtTime, err := time.Parse(time.RFC3339, followedAt)
111
111
if err != nil {
112
112
log.Println("unable to determine followed at time")
113
-
follow.FollowedAt = nil
113
+
follow.FollowedAt = time.Now()
114
114
} else {
115
-
follow.FollowedAt = &followedAtTime
115
+
follow.FollowedAt = followedAtTime
116
116
}
117
117
118
118
follows = append(follows, follow)
+9
-10
appview/db/repos.go
+9
-10
appview/db/repos.go
···
10
10
Name string
11
11
Knot string
12
12
Rkey string
13
-
Created *time.Time
13
+
Created time.Time
14
14
}
15
15
16
16
func (d *DB) GetAllRepos() ([]Repo, error) {
17
17
var repos []Repo
18
18
19
-
rows, err := d.db.Query(`select * from repos`)
19
+
rows, err := d.db.Query(`select did, name, knot, rkey, created from repos`)
20
20
if err != nil {
21
21
return nil, err
22
22
}
···
24
24
25
25
for rows.Next() {
26
26
var repo Repo
27
-
err := scanRepo(rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, repo.Created)
27
+
err := scanRepo(rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Created)
28
28
if err != nil {
29
29
return nil, err
30
30
}
···
41
41
func (d *DB) GetAllReposByDid(did string) ([]Repo, error) {
42
42
var repos []Repo
43
43
44
-
rows, err := d.db.Query(`select did, name, knot, created from repos where did = ?`, did)
44
+
rows, err := d.db.Query(`select did, name, knot, rkey, created from repos where did = ?`, did)
45
45
if err != nil {
46
46
return nil, err
47
47
}
···
49
49
50
50
for rows.Next() {
51
51
var repo Repo
52
-
err := scanRepo(rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, repo.Created)
52
+
err := scanRepo(rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Created)
53
53
if err != nil {
54
54
return nil, err
55
55
}
···
73
73
return nil, err
74
74
}
75
75
createdAtTime, _ := time.Parse(time.RFC3339, createdAt)
76
-
repo.Created = &createdAtTime
76
+
repo.Created = createdAtTime
77
77
78
78
return &repo, nil
79
79
}
···
107
107
108
108
for rows.Next() {
109
109
var repo Repo
110
-
err := scanRepo(rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, repo.Created)
110
+
err := scanRepo(rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Created)
111
111
if err != nil {
112
112
return nil, err
113
113
}
···
129
129
130
130
createdAtTime, err := time.Parse(time.RFC3339, createdAt)
131
131
if err != nil {
132
-
now := time.Now()
133
-
created = &now
132
+
*created = time.Now()
134
133
} else {
135
-
created = &createdAtTime
134
+
*created = createdAtTime
136
135
}
137
136
138
137
return nil
+4
-2
appview/db/timeline.go
+4
-2
appview/db/timeline.go
···
1
1
package db
2
2
3
3
import (
4
+
"log"
4
5
"sort"
5
6
"time"
6
7
)
···
8
9
type TimelineEvent struct {
9
10
*Repo
10
11
*Follow
11
-
EventAt *time.Time
12
+
EventAt time.Time
12
13
}
13
14
14
15
func (d *DB) MakeTimeline() ([]TimelineEvent, error) {
···
25
26
}
26
27
27
28
for _, repo := range repos {
29
+
log.Println(repo.Created)
28
30
events = append(events, TimelineEvent{
29
31
Repo: &repo,
30
32
Follow: nil,
···
41
43
}
42
44
43
45
sort.Slice(events, func(i, j int) bool {
44
-
return events[i].EventAt.After(*events[j].EventAt)
46
+
return events[i].EventAt.After(events[j].EventAt)
45
47
})
46
48
47
49
return events, nil
+1
appview/pages/pages.go
+1
appview/pages/pages.go
+12
-7
appview/pages/templates/layouts/topbar.html
+12
-7
appview/pages/templates/layouts/topbar.html
···
13
13
<a href="/repo/new" hx-boost="true" class="{{ $linkstyle }}">
14
14
<i class="w-6 h-6" data-lucide="plus"></i>
15
15
</a>
16
-
<a href="/{{ didOrHandle .Did .Handle }}" hx-boost="true" class="{{ $linkstyle }}">
17
-
{{ didOrHandle .Did .Handle }}
18
-
</a>
19
-
<a href="/logout"class="{{ $linkstyle }}">
20
-
(logout)
21
-
</a>
16
+
<details class="relative inline-block text-left">
17
+
<summary class="{{ $linkstyle }} cursor-pointer list-none">
18
+
{{ didOrHandle .Did .Handle }}
19
+
</summary>
20
+
<div class="absolute flex flex-col right-0 mt-4 p-2 w-48 bg-white border border-black z-50">
21
+
<a href="/{{ didOrHandle .Did .Handle }}"class="{{ $linkstyle }}">profile</a>
22
+
<a href="/knots"class="{{ $linkstyle }}">knots</a>
23
+
<a href="/settings"class="{{ $linkstyle }}">settings</a>
24
+
<a href="/logout" class="text-red-400 hover:text-red-700 no-underline">logout</a>
25
+
</div>
26
+
</details>
22
27
{{ else }}
23
-
<a href="/login" hx-boost="true" class="{{ $linkstyle }}">
28
+
<a href="/login" class="{{ $linkstyle }}">
24
29
login
25
30
</a>
26
31
{{ end }}
+35
-26
appview/pages/templates/timeline.html
+35
-26
appview/pages/templates/timeline.html
···
4
4
<h1>Timeline</h1>
5
5
6
6
{{ range .Timeline }}
7
+
<div class="relative
8
+
px-4
9
+
py-2
10
+
border-l
11
+
border-black
12
+
before:content-['']
13
+
before:absolute
14
+
before:w-1
15
+
before:h-1
16
+
before:bg-black
17
+
before:rounded-full
18
+
before:left-[-2.2px]
19
+
before:top-1/2
20
+
before:-translate-y-1/2
21
+
">
7
22
{{ 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>
23
+
{{ $userHandle := index $.DidHandleMap .Repo.Did }}
24
+
<div class="flex items-center">
25
+
<p class="text-gray-600">
26
+
<a href="/{{ $userHandle }}" class="no-underline hover:underline">{{ $userHandle }}</a>
27
+
created
28
+
<a href="/{{ $userHandle }}/{{ .Repo.Name }}" class="no-underline hover:underline">{{ .Repo.Name }}</a>
29
+
<time class="text-gray-700">{{ .Repo.Created | timeFmt }}</time>
30
+
</p>
31
+
</div>
20
32
{{ 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>
30
-
31
-
<time class="text-sm text-gray-700"
32
-
>{{ .Follow.FollowedAt | timeFmt }}</time
33
-
>
34
-
</div>
33
+
{{ $userHandle := index $.DidHandleMap .Follow.UserDid }}
34
+
{{ $subjectHandle := index $.DidHandleMap .Follow.SubjectDid }}
35
+
<div class="flex items-center">
36
+
<p class="text-gray-600">
37
+
<a href="/{{ $userHandle }}" class="no-underline hover:underline">{{ $userHandle }}</a>
38
+
followed
39
+
<a href="/{{ $subjectHandle }}" class="no-underline hover:underline">{{ $subjectHandle }}</a>
40
+
<time class="text-gray-700">{{ .Follow.FollowedAt | timeFmt }}</time>
41
+
</p>
42
+
</div>
35
43
{{ end }}
44
+
</div>
36
45
{{ end }}
37
46
38
47
{{ end }}
+1
-1
appview/pages/templates/user/profile.html
+1
-1
appview/pages/templates/user/profile.html
···
24
24
<div class="text-sm mb-4">
25
25
<span>{{ .ProfileStats.Followers }} followers</span>
26
26
<div class="inline-block px-1 select-none after:content-['·']"></div>
27
-
<span>following {{ .ProfileStats.Following }}</span>
27
+
<span>{{ .ProfileStats.Following }} following</span>
28
28
</div>
29
29
<p class="text-xs font-bold py-2">REPOS</p>
30
30
<div id="repos" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
+22
appview/state/state.go
+22
appview/state/state.go
···
165
165
s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.")
166
166
}
167
167
168
+
var didsToResolve []string
169
+
for _, ev := range timeline {
170
+
if ev.Repo != nil {
171
+
didsToResolve = append(didsToResolve, ev.Repo.Did)
172
+
}
173
+
if ev.Follow != nil {
174
+
didsToResolve = append(didsToResolve, ev.Follow.UserDid)
175
+
didsToResolve = append(didsToResolve, ev.Follow.SubjectDid)
176
+
}
177
+
}
178
+
179
+
resolvedIds := s.resolver.ResolveIdents(r.Context(), didsToResolve)
180
+
didHandleMap := make(map[string]string)
181
+
for _, identity := range resolvedIds {
182
+
if !identity.Handle.IsInvalidHandle() {
183
+
didHandleMap[identity.DID.String()] = fmt.Sprintf("@%s", identity.Handle.String())
184
+
} else {
185
+
didHandleMap[identity.DID.String()] = identity.DID.String()
186
+
}
187
+
}
188
+
168
189
s.pages.Timeline(w, pages.TimelineParams{
169
190
LoggedInUser: user,
170
191
Timeline: timeline,
192
+
DidHandleMap: didHandleMap,
171
193
})
172
194
173
195
return