appview/pages: add labels to pulls #615

merged
opened by oppi.li targeting master from push-nwrnkztxkovl
Changed files
+171 -43
appview
db
issues
models
pages
pulls
+19 -1
appview/db/pulls.go
···
package db
import (
"database/sql"
"fmt"
"maps"
···
p.Submissions = submissions
}
}
orderedByPullId := []*models.Pull{}
for _, p := range pulls {
···
}
}
-
// order the submissions by pull_at
m := make(map[syntax.ATURI][]*models.PullSubmission)
for _, s := range submissionMap {
m[s.PullAt] = append(m[s.PullAt], s)
}
return m, nil
}
···
package db
import (
+
"cmp"
"database/sql"
"fmt"
"maps"
···
p.Submissions = submissions
}
}
+
// collect allLabels for each issue
+
allLabels, err := GetLabels(e, FilterIn("subject", pullAts))
+
if err != nil {
+
return nil, fmt.Errorf("failed to query labels: %w", err)
+
}
+
for pullAt, labels := range allLabels {
+
if p, ok := pulls[pullAt]; ok {
+
p.Labels = labels
+
}
+
}
orderedByPullId := []*models.Pull{}
for _, p := range pulls {
···
}
}
+
// group the submissions by pull_at
m := make(map[syntax.ATURI][]*models.PullSubmission)
for _, s := range submissionMap {
m[s.PullAt] = append(m[s.PullAt], s)
}
+
// sort each one by round number
+
for _, s := range m {
+
slices.SortFunc(s, func(a, b *models.PullSubmission) int {
+
return cmp.Compare(a.RoundNumber, b.RoundNumber)
+
})
+
}
+
return m, nil
}
+5 -1
appview/issues/issues.go
···
return
}
-
labelDefs, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", f.Repo.Labels))
if err != nil {
log.Println("failed to fetch labels", err)
rp.pages.Error503(w)
···
return
}
+
labelDefs, err := db.GetLabelDefinitions(
+
rp.db,
+
db.FilterIn("at_uri", f.Repo.Labels),
+
db.FilterContains("scope", tangled.RepoIssueNSID),
+
)
if err != nil {
log.Println("failed to fetch labels", err)
rp.pages.Error503(w)
+44 -1
appview/models/pull.go
···
PullSource *PullSource
// optionally, populate this when querying for reverse mappings
-
Repo *Repo
}
func (p Pull) AsRecord() tangled.RepoPull {
···
return p.StackId != ""
}
func (s PullSubmission) IsFormatPatch() bool {
return patchutil.IsFormatPatch(s.Patch)
}
···
return patches
}
type Stack []*Pull
// position of this pull in the stack
···
PullSource *PullSource
// optionally, populate this when querying for reverse mappings
+
Labels LabelState
+
Repo *Repo
}
func (p Pull) AsRecord() tangled.RepoPull {
···
return p.StackId != ""
}
+
func (p *Pull) Participants() []string {
+
participantSet := make(map[string]struct{})
+
participants := []string{}
+
+
addParticipant := func(did string) {
+
if _, exists := participantSet[did]; !exists {
+
participantSet[did] = struct{}{}
+
participants = append(participants, did)
+
}
+
}
+
+
addParticipant(p.OwnerDid)
+
+
for _, s := range p.Submissions {
+
for _, sp := range s.Participants() {
+
addParticipant(sp)
+
}
+
}
+
+
return participants
+
}
+
func (s PullSubmission) IsFormatPatch() bool {
return patchutil.IsFormatPatch(s.Patch)
}
···
return patches
}
+
func (s *PullSubmission) Participants() []string {
+
participantSet := make(map[string]struct{})
+
participants := []string{}
+
+
addParticipant := func(did string) {
+
if _, exists := participantSet[did]; !exists {
+
participantSet[did] = struct{}{}
+
participants = append(participants, did)
+
}
+
}
+
+
addParticipant(s.PullAt.Authority().String())
+
+
for _, c := range s.Comments {
+
addParticipant(c.OwnerDid)
+
}
+
+
return participants
+
}
+
type Stack []*Pull
// position of this pull in the stack
+3
appview/pages/pages.go
···
FilteringBy models.PullState
Stacks map[string]models.Stack
Pipelines map[string]models.Pipeline
}
func (p *Pages) RepoPulls(w io.Writer, params RepoPullsParams) error {
···
OrderedReactionKinds []models.ReactionKind
Reactions map[models.ReactionKind]int
UserReacted map[models.ReactionKind]bool
}
func (p *Pages) RepoSinglePull(w io.Writer, params RepoSinglePullParams) error {
···
FilteringBy models.PullState
Stacks map[string]models.Stack
Pipelines map[string]models.Pipeline
+
LabelDefs map[string]*models.LabelDefinition
}
func (p *Pages) RepoPulls(w io.Writer, params RepoPullsParams) error {
···
OrderedReactionKinds []models.ReactionKind
Reactions map[models.ReactionKind]int
UserReacted map[models.ReactionKind]bool
+
+
LabelDefs map[string]*models.LabelDefinition
}
func (p *Pages) RepoSinglePull(w io.Writer, params RepoSinglePullParams) error {
+1 -1
appview/pages/templates/repo/fragments/labelPanel.html
···
{{ define "repo/fragments/labelPanel" }}
-
<div id="label-panel" class="flex flex-col gap-6">
{{ template "basicLabels" . }}
{{ template "kvLabels" . }}
</div>
···
{{ define "repo/fragments/labelPanel" }}
+
<div id="label-panel" class="flex flex-col gap-6 px-6 md:px-0">
{{ template "basicLabels" . }}
{{ template "kvLabels" . }}
</div>
+26
appview/pages/templates/repo/fragments/participants.html
···
···
+
{{ define "repo/fragments/participants" }}
+
{{ $all := . }}
+
{{ $ps := take $all 5 }}
+
<div class="px-6 md:px-0">
+
<div class="py-1 flex items-center text-sm">
+
<span class="font-bold text-gray-500 dark:text-gray-400 capitalize">Participants</span>
+
<span class="bg-gray-200 dark:bg-gray-700 rounded py-1/2 px-1 ml-1">{{ len $all }}</span>
+
</div>
+
<div class="flex items-center -space-x-3 mt-2">
+
{{ $c := "z-50 z-40 z-30 z-20 z-10" }}
+
{{ range $i, $p := $ps }}
+
<img
+
src="{{ tinyAvatar . }}"
+
alt=""
+
class="rounded-full h-8 w-8 mr-1 border-2 border-gray-100 dark:border-gray-900 z-{{sub 5 $i}}0"
+
/>
+
{{ end }}
+
+
{{ if gt (len $all) 5 }}
+
<span class="pl-4 text-gray-500 dark:text-gray-400 text-sm">
+
+{{ sub (len $all) 5 }}
+
</span>
+
{{ end }}
+
</div>
+
</div>
+
{{ end }}
+1 -27
appview/pages/templates/repo/issues/issue.html
···
"Defs" $.LabelDefs
"Subject" $.Issue.AtUri
"State" $.Issue.Labels) }}
-
{{ template "issueParticipants" . }}
</div>
</div>
{{ end }}
···
</div>
{{ end }}
-
{{ define "issueParticipants" }}
-
{{ $all := .Issue.Participants }}
-
{{ $ps := take $all 5 }}
-
<div>
-
<div class="py-1 flex items-center text-sm">
-
<span class="font-bold text-gray-500 dark:text-gray-400 capitalize">Participants</span>
-
<span class="bg-gray-200 dark:bg-gray-700 rounded py-1/2 px-1 ml-1">{{ len $all }}</span>
-
</div>
-
<div class="flex items-center -space-x-3 mt-2">
-
{{ $c := "z-50 z-40 z-30 z-20 z-10" }}
-
{{ range $i, $p := $ps }}
-
<img
-
src="{{ tinyAvatar . }}"
-
alt=""
-
class="rounded-full h-8 w-8 mr-1 border-2 border-gray-100 dark:border-gray-900 z-{{sub 5 $i}}0"
-
/>
-
{{ end }}
-
-
{{ if gt (len $all) 5 }}
-
<span class="pl-4 text-gray-500 dark:text-gray-400 text-sm">
-
+{{ sub (len $all) 5 }}
-
</span>
-
{{ end }}
-
</div>
-
</div>
-
{{ end }}
{{ define "repoAfter" }}
<div class="flex flex-col gap-4 mt-4">
···
"Defs" $.LabelDefs
"Subject" $.Issue.AtUri
"State" $.Issue.Labels) }}
+
{{ template "repo/fragments/participants" $.Issue.Participants }}
</div>
</div>
{{ end }}
···
</div>
{{ end }}
{{ define "repoAfter" }}
<div class="flex flex-col gap-4 mt-4">
+30 -12
appview/pages/templates/repo/pulls/pull.html
···
{{ template "repo/fragments/og" (dict "RepoInfo" .RepoInfo "Title" $title "Url" $url) }}
{{ end }}
{{ define "repoContent" }}
{{ template "repo/pulls/fragments/pullHeader" . }}
···
{{ with $item }}
<details {{ if eq $idx $lastIdx }}open{{ end }}>
<summary id="round-#{{ .RoundNumber }}" class="list-none cursor-pointer">
-
<div class="flex flex-wrap gap-2 items-center">
<!-- round number -->
<div class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-3 py-2 dark:text-white">
<span class="flex items-center">{{ i "hash" "w-4 h-4" }}{{ .RoundNumber }}</span>
</div>
<!-- round summary -->
-
<div class="rounded drop-shadow-sm bg-white dark:bg-gray-800 p-2 text-gray-500 dark:text-gray-400">
<span class="gap-1 flex items-center">
{{ $owner := resolve $.Pull.OwnerDid }}
{{ $re := "re" }}
···
<span class="hidden md:inline">diff</span>
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
</a>
-
{{ if not (eq .RoundNumber 0) }}
-
<a class="btn flex items-center gap-2 no-underline hover:no-underline p-2 group"
-
hx-boost="true"
-
href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{.RoundNumber}}/interdiff">
-
{{ i "chevrons-left-right-ellipsis" "w-4 h-4 rotate-90" }}
-
<span class="hidden md:inline">interdiff</span>
-
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
-
</a>
-
<span id="interdiff-error-{{.RoundNumber}}"></span>
{{ end }}
</div>
</summary>
···
<div class="md:pl-[3.5rem] flex flex-col gap-2 mt-2 relative">
{{ range $cidx, $c := .Comments }}
-
<div id="comment-{{$c.ID}}" class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-2 px-4 relative w-full md:max-w-3/5 md:w-fit">
{{ if gt $cidx 0 }}
<div class="absolute left-8 -top-2 w-px h-2 bg-gray-300 dark:bg-gray-600"></div>
{{ end }}
···
{{ template "repo/fragments/og" (dict "RepoInfo" .RepoInfo "Title" $title "Url" $url) }}
{{ end }}
+
{{ define "repoContentLayout" }}
+
<div class="grid grid-cols-1 md:grid-cols-10 gap-4 w-full">
+
<div class="col-span-1 md:col-span-8">
+
<section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto dark:text-white">
+
{{ block "repoContent" . }}{{ end }}
+
</section>
+
{{ block "repoAfter" . }}{{ end }}
+
</div>
+
<div class="col-span-1 md:col-span-2 flex flex-col gap-6">
+
{{ template "repo/fragments/labelPanel"
+
(dict "RepoInfo" $.RepoInfo
+
"Defs" $.LabelDefs
+
"Subject" $.Pull.PullAt
+
"State" $.Pull.Labels) }}
+
{{ template "repo/fragments/participants" $.Pull.Participants }}
+
</div>
+
</div>
+
{{ end }}
{{ define "repoContent" }}
{{ template "repo/pulls/fragments/pullHeader" . }}
···
{{ with $item }}
<details {{ if eq $idx $lastIdx }}open{{ end }}>
<summary id="round-#{{ .RoundNumber }}" class="list-none cursor-pointer">
+
<div class="flex flex-wrap gap-2 items-stretch">
<!-- round number -->
<div class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-3 py-2 dark:text-white">
<span class="flex items-center">{{ i "hash" "w-4 h-4" }}{{ .RoundNumber }}</span>
</div>
<!-- round summary -->
+
<div class="flex-1 rounded drop-shadow-sm bg-white dark:bg-gray-800 p-2 text-gray-500 dark:text-gray-400">
<span class="gap-1 flex items-center">
{{ $owner := resolve $.Pull.OwnerDid }}
{{ $re := "re" }}
···
<span class="hidden md:inline">diff</span>
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
</a>
+
{{ if ne $idx 0 }}
+
<a class="btn flex items-center gap-2 no-underline hover:no-underline p-2 group"
+
hx-boost="true"
+
href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{.RoundNumber}}/interdiff">
+
{{ i "chevrons-left-right-ellipsis" "w-4 h-4 rotate-90" }}
+
<span class="hidden md:inline">interdiff</span>
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
+
</a>
{{ end }}
+
<span id="interdiff-error-{{.RoundNumber}}"></span>
</div>
</summary>
···
<div class="md:pl-[3.5rem] flex flex-col gap-2 mt-2 relative">
{{ range $cidx, $c := .Comments }}
+
<div id="comment-{{$c.ID}}" class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-2 px-4 relative w-full">
{{ if gt $cidx 0 }}
<div class="absolute left-8 -top-2 w-px h-2 bg-gray-300 dark:bg-gray-600"></div>
{{ end }}
+7
appview/pages/templates/repo/pulls/pulls.html
···
<span class="before:content-['·']"></span>
{{ template "repo/pipelines/fragments/pipelineSymbol" $pipeline }}
{{ end }}
</div>
</div>
{{ if .StackId }}
···
<span class="before:content-['·']"></span>
{{ template "repo/pipelines/fragments/pipelineSymbol" $pipeline }}
{{ end }}
+
+
{{ $state := .Labels }}
+
{{ range $k, $d := $.LabelDefs }}
+
{{ range $v, $s := $state.GetValSet $d.AtUri.String }}
+
{{ template "labels/fragments/label" (dict "def" $d "val" $v "withPrefix" true) }}
+
{{ end }}
+
{{ end }}
</div>
</div>
{{ if .StackId }}
+35
appview/pulls/pulls.go
···
userReactions = db.GetReactionStatusMap(s.db, user.Did, pull.PullAt())
}
s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{
LoggedInUser: user,
RepoInfo: repoInfo,
···
OrderedReactionKinds: models.OrderedReactionKinds,
Reactions: reactionCountMap,
UserReacted: userReactions,
})
}
···
m[p.Sha] = p
}
s.pages.RepoPulls(w, pages.RepoPullsParams{
LoggedInUser: s.oauth.GetUser(r),
RepoInfo: f.RepoInfo(user),
Pulls: pulls,
FilteringBy: state,
Stacks: stacks,
Pipelines: m,
···
userReactions = db.GetReactionStatusMap(s.db, user.Did, pull.PullAt())
}
+
labelDefs, err := db.GetLabelDefinitions(
+
s.db,
+
db.FilterIn("at_uri", f.Repo.Labels),
+
db.FilterContains("scope", tangled.RepoPullNSID),
+
)
+
if err != nil {
+
log.Println("failed to fetch labels", err)
+
s.pages.Error503(w)
+
return
+
}
+
+
defs := make(map[string]*models.LabelDefinition)
+
for _, l := range labelDefs {
+
defs[l.AtUri().String()] = &l
+
}
+
s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{
LoggedInUser: user,
RepoInfo: repoInfo,
···
OrderedReactionKinds: models.OrderedReactionKinds,
Reactions: reactionCountMap,
UserReacted: userReactions,
+
+
LabelDefs: defs,
})
}
···
m[p.Sha] = p
}
+
labelDefs, err := db.GetLabelDefinitions(
+
s.db,
+
db.FilterIn("at_uri", f.Repo.Labels),
+
db.FilterContains("scope", tangled.RepoPullNSID),
+
)
+
if err != nil {
+
log.Println("failed to fetch labels", err)
+
s.pages.Error503(w)
+
return
+
}
+
+
defs := make(map[string]*models.LabelDefinition)
+
for _, l := range labelDefs {
+
defs[l.AtUri().String()] = &l
+
}
+
s.pages.RepoPulls(w, pages.RepoPullsParams{
LoggedInUser: s.oauth.GetUser(r),
RepoInfo: f.RepoInfo(user),
Pulls: pulls,
+
LabelDefs: defs,
FilteringBy: state,
Stacks: stacks,
Pipelines: m,