Signed-off-by: Anirudh Oppiliappan anirudh@tangled.sh
REBASED
appview/db/timeline.go
REBASED
appview/db/timeline.go
This patch was likely rebased, as context lines do not match.
REVERTED
appview/pages/templates/timeline/fragments/timeline.html
REVERTED
appview/pages/templates/timeline/fragments/timeline.html
···
13
13
{{ with $e }}
14
14
<div class="flex flex-col divide-y divide-gray-200 dark:divide-gray-700 border border-gray-200 dark:border-gray-700 rounded-sm">
15
15
{{ if .Repo }}
16
+
{{ template "timeline/fragments/repoEvent" (list $ .Repo .Source) }}
16
-
{{ template "timeline/fragments/repoEvent" (list $ .) }}
17
17
{{ else if .Star }}
18
18
{{ template "timeline/fragments/starEvent" (list $ .Star) }}
19
19
{{ else if .Follow }}
···
29
29
30
30
{{ define "timeline/fragments/repoEvent" }}
31
31
{{ $root := index . 0 }}
32
+
{{ $repo := index . 1 }}
33
+
{{ $source := index . 2 }}
32
-
{{ $event := index . 1 }}
33
-
{{ $repo := $event.Repo }}
34
-
{{ $source := $event.Source }}
35
34
{{ $userHandle := resolve $repo.Did }}
36
35
<div class="pl-6 py-2 bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-300 flex flex-wrap items-center gap-2 text-sm">
37
36
{{ template "user/fragments/picHandleLink" $repo.Did }}
···
52
51
<span class="text-gray-700 dark:text-gray-400 text-xs">{{ template "repo/fragments/time" $repo.Created }}</span>
53
52
</div>
54
53
{{ with $repo }}
54
+
{{ template "user/fragments/repoCard" (list $root . true) }}
55
-
{{ template "user/fragments/repoCard" (list $root . true true (dict "IsStarred" $event.IsStarred "RepoAt" .RepoAt "Stats" (dict "StarCount" $event.StarCount))) }}
56
55
{{ end }}
57
56
{{ end }}
58
57
REVERTED
appview/pages/templates/user/fragments/repoCard.html
REVERTED
appview/pages/templates/user/fragments/repoCard.html
···
2
2
{{ $root := index . 0 }}
3
3
{{ $repo := index . 1 }}
4
4
{{ $fullName := index . 2 }}
5
-
{{ $starButton := false }}
6
-
{{ $starData := dict }}
7
-
{{ if gt (len .) 3 }}
8
-
{{ $starButton = index . 3 }}
9
-
{{ if gt (len .) 4 }}
10
-
{{ $starData = index . 4 }}
11
-
{{ end }}
12
-
{{ end }}
13
5
14
6
{{ with $repo }}
15
7
<div class="py-4 px-6 gap-1 flex flex-col drop-shadow-sm rounded bg-white dark:bg-gray-800 min-h-32">
8
+
<div class="font-medium dark:text-white flex items-center">
16
-
<div class="font-medium dark:text-white flex items-center justify-between">
17
-
<div class="flex items-center">
18
9
{{ if .Source }}
19
10
{{ i "git-fork" "w-4 h-4 mr-1.5 shrink-0" }}
20
11
{{ else }}
21
12
{{ i "book-marked" "w-4 h-4 mr-1.5 shrink-0" }}
22
13
{{ end }}
23
14
15
+
{{ $repoOwner := resolve .Did }}
16
+
{{- if $fullName -}}
17
+
<a href="/{{ $repoOwner }}/{{ .Name }}" class="truncate">{{ $repoOwner }}/{{ .Name }}</a>
18
+
{{- else -}}
19
+
<a href="/{{ $repoOwner }}/{{ .Name }}" class="truncate">{{ .Name }}</a>
20
+
{{- end -}}
24
-
{{ $repoOwner := resolve .Did }}
25
-
{{- if $fullName -}}
26
-
<a href="/{{ $repoOwner }}/{{ .Name }}" class="truncate">{{ $repoOwner }}/{{ .Name }}</a>
27
-
{{- else -}}
28
-
<a href="/{{ $repoOwner }}/{{ .Name }}" class="truncate">{{ .Name }}</a>
29
-
{{- end -}}
30
-
</div>
31
-
32
-
{{ if and $starButton $root.LoggedInUser }}
33
-
{{ template "repo/fragments/repoStar" $starData }}
34
-
{{ end }}
35
21
</div>
36
22
{{ with .Description }}
37
23
<div class="text-gray-600 dark:text-gray-300 text-sm line-clamp-2">
NEW
appview/db/follow.go
NEW
appview/db/follow.go
···
229
229
}
230
230
}
231
231
232
+
func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]FollowStatus, error) {
233
+
if len(subjectDids) == 0 || userDid == "" {
234
+
return make(map[string]FollowStatus), nil
235
+
}
236
+
237
+
result := make(map[string]FollowStatus)
238
+
239
+
for _, subjectDid := range subjectDids {
240
+
if userDid == subjectDid {
241
+
result[subjectDid] = IsSelf
242
+
} else {
243
+
result[subjectDid] = IsNotFollowing
244
+
}
245
+
}
246
+
247
+
var querySubjects []string
248
+
for _, subjectDid := range subjectDids {
249
+
if userDid != subjectDid {
250
+
querySubjects = append(querySubjects, subjectDid)
251
+
}
252
+
}
253
+
254
+
if len(querySubjects) == 0 {
255
+
return result, nil
256
+
}
257
+
258
+
placeholders := make([]string, len(querySubjects))
259
+
args := make([]any, len(querySubjects)+1)
260
+
args[0] = userDid
261
+
262
+
for i, subjectDid := range querySubjects {
263
+
placeholders[i] = "?"
264
+
args[i+1] = subjectDid
265
+
}
266
+
267
+
query := fmt.Sprintf(`
268
+
SELECT subject_did
269
+
FROM follows
270
+
WHERE user_did = ? AND subject_did IN (%s)
271
+
`, strings.Join(placeholders, ","))
272
+
273
+
rows, err := e.Query(query, args...)
274
+
if err != nil {
275
+
return nil, err
276
+
}
277
+
defer rows.Close()
278
+
279
+
for rows.Next() {
280
+
var subjectDid string
281
+
if err := rows.Scan(&subjectDid); err != nil {
282
+
return nil, err
283
+
}
284
+
result[subjectDid] = IsFollowing
285
+
}
286
+
287
+
return result, nil
288
+
}
289
+
232
290
func GetFollowStatus(e Execer, userDid, subjectDid string) FollowStatus {
233
-
if userDid == subjectDid {
234
-
return IsSelf
235
-
} else if _, err := GetFollow(e, userDid, subjectDid); err != nil {
291
+
statuses, err := getFollowStatuses(e, userDid, []string{subjectDid})
292
+
if err != nil {
236
293
return IsNotFollowing
237
-
} else {
238
-
return IsFollowing
239
294
}
295
+
return statuses[subjectDid]
296
+
}
297
+
298
+
func GetFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]FollowStatus, error) {
299
+
return getFollowStatuses(e, userDid, subjectDids)
240
300
}
NEW
appview/db/star.go
NEW
appview/db/star.go
···
94
94
return stars, nil
95
95
}
96
96
97
+
// getStarStatuses returns a map of repo URIs to star status for a given user
98
+
// This is an internal helper function to avoid N+1 queries
99
+
func getStarStatuses(e Execer, userDid string, repoAts []syntax.ATURI) (map[string]bool, error) {
100
+
if len(repoAts) == 0 || userDid == "" {
101
+
return make(map[string]bool), nil
102
+
}
103
+
104
+
placeholders := make([]string, len(repoAts))
105
+
args := make([]any, len(repoAts)+1)
106
+
args[0] = userDid
107
+
108
+
for i, repoAt := range repoAts {
109
+
placeholders[i] = "?"
110
+
args[i+1] = repoAt.String()
111
+
}
112
+
113
+
query := fmt.Sprintf(`
114
+
SELECT repo_at
115
+
FROM stars
116
+
WHERE starred_by_did = ? AND repo_at IN (%s)
117
+
`, strings.Join(placeholders, ","))
118
+
119
+
rows, err := e.Query(query, args...)
120
+
if err != nil {
121
+
return nil, err
122
+
}
123
+
defer rows.Close()
124
+
125
+
result := make(map[string]bool)
126
+
// Initialize all repos as not starred
127
+
for _, repoAt := range repoAts {
128
+
result[repoAt.String()] = false
129
+
}
130
+
131
+
// Mark starred repos as true
132
+
for rows.Next() {
133
+
var repoAt string
134
+
if err := rows.Scan(&repoAt); err != nil {
135
+
return nil, err
136
+
}
137
+
result[repoAt] = true
138
+
}
139
+
140
+
return result, nil
141
+
}
142
+
97
143
func GetStarStatus(e Execer, userDid string, repoAt syntax.ATURI) bool {
98
-
if _, err := GetStar(e, userDid, repoAt); err != nil {
144
+
statuses, err := getStarStatuses(e, userDid, []syntax.ATURI{repoAt})
145
+
if err != nil {
99
146
return false
100
-
} else {
101
-
return true
102
147
}
148
+
return statuses[repoAt.String()]
103
149
}
104
150
151
+
// GetStarStatuses returns a map of repo URIs to star status for a given user
152
+
func GetStarStatuses(e Execer, userDid string, repoAts []syntax.ATURI) (map[string]bool, error) {
153
+
return getStarStatuses(e, userDid, repoAts)
154
+
}
105
155
func GetStars(e Execer, limit int, filters ...filter) ([]Star, error) {
106
156
var conditions []string
107
157
var args []any