back interdiff of round #2 and #1

appview/{pages,db}: show star/unstar buttons on the timeline #554

merged
opened by anirudh.fi targeting master from push-qrltzqmlrlln
files
appview
db
pages
templates
timeline
fragments
user
fragments
REBASED
appview/db/timeline.go

This patch was likely rebased, as context lines do not match.

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
··· 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
··· 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
··· 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