Monorepo for Tangled tangled.org

appview: pull: add "relation-chain" style attachment to pulls interface

Changed files
+184 -61
appview
db
pages
templates
state
+22
appview/db/pulls.go
··· 1063 1063 return nil 1064 1064 } 1065 1065 1066 + // all pulls above this pull (including self) in this stack 1067 + func (stack Stack) Above(pull *Pull) Stack { 1068 + position := stack.Position(pull) 1069 + 1070 + if position < 0 { 1071 + return nil 1072 + } 1073 + 1074 + return stack[:position+1] 1075 + } 1076 + 1077 + // all pulls below this pull (excluding self) in this stack 1078 + func (stack Stack) StrictlyAbove(pull *Pull) Stack { 1079 + above := stack.Above(pull) 1080 + 1081 + if len(above) > 0 { 1082 + return above[:len(above)-1] 1083 + } 1084 + 1085 + return nil 1086 + } 1087 + 1066 1088 // the combined format-patches of all the newest submissions in this stack 1067 1089 func (stack Stack) CombinedPatch() string { 1068 1090 // go in reverse order because the bottom of the stack is the last element in the slice
+2
appview/pages/pages.go
··· 743 743 Active string 744 744 DidHandleMap map[string]string 745 745 Pull *db.Pull 746 + Stack db.Stack 746 747 MergeCheck types.MergeCheckResponse 747 748 ResubmitCheck ResubmitResult 748 749 } ··· 757 758 DidHandleMap map[string]string 758 759 RepoInfo repoinfo.RepoInfo 759 760 Pull *db.Pull 761 + Stack db.Stack 760 762 Diff *types.NiceDiff 761 763 Round int 762 764 Submission *db.PullSubmission
+94 -57
appview/pages/templates/repo/pulls/fragments/pullHeader.html
··· 1 1 {{ define "repo/pulls/fragments/pullHeader" }} 2 - <header class="pb-4"> 3 - <h1 class="text-2xl dark:text-white"> 4 - {{ .Pull.Title }} 5 - <span class="text-gray-500 dark:text-gray-400">#{{ .Pull.PullId }}</span> 6 - </h1> 7 - </header> 2 + <header class="flex items-center gap-2 pb-2"> 3 + {{ block "pullState" .Pull }} {{ end }} 4 + <h1 class="text-2xl dark:text-white"> 5 + <span class="text-gray-500 dark:text-gray-400">#{{ .Pull.PullId }}</span> 6 + {{ .Pull.Title }} 7 + </h1> 8 + </header> 8 9 9 - {{ $bgColor := "bg-gray-800 dark:bg-gray-700" }} 10 - {{ $icon := "ban" }} 10 + <section class=""> 11 + <div class="flex items-center gap-2"> 12 + <span class="text-gray-500 dark:text-gray-400 text-sm"> 13 + opened by 14 + {{ $owner := index $.DidHandleMap .Pull.OwnerDid }} 15 + <a href="/{{ $owner }}" class="no-underline hover:underline">{{ $owner }}</a> 16 + <span class="select-none before:content-['\00B7']"></span> 17 + <time>{{ .Pull.Created | timeFmt }}</time> 18 + <span class="select-none before:content-['\00B7']"></span> 19 + <span> 20 + targeting 21 + <span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"> 22 + <a href="/{{ .RepoInfo.FullName }}/tree/{{ .Pull.TargetBranch }}" class="no-underline hover:underline">{{ .Pull.TargetBranch }}</a> 23 + </span> 24 + </span> 25 + {{ if not .Pull.IsPatchBased }} 26 + from 27 + <span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"> 28 + {{ if .Pull.IsForkBased }} 29 + {{ if .Pull.PullSource.Repo }} 30 + <a href="/{{ $owner }}/{{ .Pull.PullSource.Repo.Name }}" class="no-underline hover:underline">{{ $owner }}/{{ .Pull.PullSource.Repo.Name }}</a>: 31 + {{- else -}} 32 + <span class="italic">[deleted fork]</span> 33 + {{- end -}} 34 + {{- end -}} 35 + {{- .Pull.PullSource.Branch -}} 36 + </span> 37 + {{ end }} 38 + </span> 39 + </div> 11 40 12 - {{ if .Pull.State.IsOpen }} 13 - {{ $bgColor = "bg-green-600 dark:bg-green-700" }} 14 - {{ $icon = "git-pull-request" }} 15 - {{ else if .Pull.State.IsMerged }} 16 - {{ $bgColor = "bg-purple-600 dark:bg-purple-700" }} 17 - {{ $icon = "git-merge" }} 41 + {{ if .Pull.Body }} 42 + <article id="body" class="mt-2 prose dark:prose-invert"> 43 + {{ .Pull.Body | markdown }} 44 + </article> 45 + {{ end }} 46 + </section> 47 + 48 + {{ end }} 49 + 50 + {{ define "singlePullHeader" }} 51 + {{ end }} 52 + 53 + {{ define "stackedPullHeader" }} 54 + <div class="border border-gray-200 dark:border-gray-700 rounded-sm"> 55 + {{ block "stackedPullHeaderAbove" . }} {{ end }} 56 + <div class="p-2"> 57 + {{ block "singlePullHeader" . }} {{ end }} 58 + </div> 59 + {{ block "stackedPullHeaderBelow" . }} {{ end }} 60 + </div> 61 + {{ end }} 62 + 63 + {{ define "stackedPullHeaderAbove" }} 64 + {{ if .Pull.IsStacked }} 65 + {{ $above := .Stack.StrictlyAbove .Pull }} 66 + <div class="flex flex-col"> 67 + {{ range $pull := $above }} 68 + <div class="border-b border-gray-200 dark:border-gray-700 p-2"> 69 + {{ block "summarizedHeader" (list $pull $) }} {{ end }} 70 + </div> 71 + {{ end }} 72 + </div> 73 + {{ end }} 18 74 {{ end }} 19 75 20 - <section class="mt-2"> 21 - <div class="flex items-center gap-2"> 22 - <div 23 - id="state" 24 - class="inline-flex items-center rounded px-3 py-1 {{ $bgColor }}" 25 - > 26 - {{ i $icon "w-4 h-4 mr-1.5 text-white" }} 27 - <span class="text-white">{{ .Pull.State.String }}</span> 76 + {{ define "stackedPullHeaderBelow" }} 77 + {{ if .Pull.IsStacked }} 78 + {{ $below := .Stack.StrictlyBelow .Pull }} 79 + <div class="flex flex-col"> 80 + {{ range $pull := $below }} 81 + <div class="border-t border-gray-200 dark:border-gray-700 p-2"> 82 + {{ block "summarizedHeader" (list $pull $) }} {{ end }} 28 83 </div> 29 - <span class="text-gray-500 dark:text-gray-400 text-sm"> 30 - opened by 31 - {{ $owner := index $.DidHandleMap .Pull.OwnerDid }} 32 - <a href="/{{ $owner }}" class="no-underline hover:underline" 33 - >{{ $owner }}</a 34 - > 35 - <span class="select-none before:content-['\00B7']"></span> 36 - <time>{{ .Pull.Created | timeFmt }}</time> 37 - <span class="select-none before:content-['\00B7']"></span> 38 - <span> 39 - targeting 40 - <span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"> 41 - <a href="/{{ .RepoInfo.FullName }}/tree/{{ .Pull.TargetBranch }}" class="no-underline hover:underline">{{ .Pull.TargetBranch }}</a> 42 - </span> 43 - </span> 44 - {{ if not .Pull.IsPatchBased }} 45 - from 46 - <span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"> 47 - {{ if .Pull.IsForkBased }} 48 - {{ if .Pull.PullSource.Repo }} 49 - <a href="/{{ $owner }}/{{ .Pull.PullSource.Repo.Name }}" class="no-underline hover:underline">{{ $owner }}/{{ .Pull.PullSource.Repo.Name }}</a>: 50 - {{- else -}} 51 - <span class="italic">[deleted fork]</span> 52 - {{- end -}} 53 - {{- end -}} 54 - {{- .Pull.PullSource.Branch -}} 55 - </span> 56 - {{ end }} 57 - </span> 84 + {{ end }} 58 85 </div> 86 + {{ end }} 87 + {{ end }} 59 88 60 - {{ if .Pull.Body }} 61 - <article id="body" class="mt-8 prose dark:prose-invert"> 62 - {{ .Pull.Body | markdown }} 63 - </article> 64 - {{ end }} 65 - </section> 89 + {{ define "pullState" }} 90 + {{ $bgColor := "bg-gray-800 dark:bg-gray-700" }} 91 + {{ $icon := "ban" }} 66 92 93 + {{ if .State.IsOpen }} 94 + {{ $bgColor = "bg-green-600 dark:bg-green-700" }} 95 + {{ $icon = "git-pull-request" }} 96 + {{ else if .State.IsMerged }} 97 + {{ $bgColor = "bg-purple-600 dark:bg-purple-700" }} 98 + {{ $icon = "git-merge" }} 99 + {{ end }} 67 100 101 + <div id="state" class="inline-flex items-center rounded px-3 py-1 {{ $bgColor }}" > 102 + {{ i $icon "w-4 h-4 mr-1.5 text-white" }} 103 + <span class="text-white ">{{ .State.String }}</span> 104 + </div> 68 105 {{ end }}
+47
appview/pages/templates/repo/pulls/fragments/pullStack.html
··· 1 + {{ define "repo/pulls/fragments/pullStack" }} 2 + <div class="grid grid-cols-1 rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700"> 3 + {{ range $pull := .Stack }} 4 + {{ $isCurrent := false }} 5 + {{ with $.Pull }} 6 + {{ $isCurrent = eq $pull.PullId $.Pull.PullId }} 7 + {{ end }} 8 + <div class="flex gap-2 items-center p-2"> 9 + {{ block "summarizedHeader" (list $pull $) }} {{ end }} 10 + {{ if $isCurrent }} 11 + {{ i "arrow-left" "w-4 h-4" }} 12 + {{ end }} 13 + </div> 14 + {{ end }} 15 + </div> 16 + {{ end }} 17 + 18 + {{ define "summarizedHeader" }} 19 + {{ $pull := index . 0 }} 20 + {{ $root := index . 1 }} 21 + <a href="/{{ $root.RepoInfo.FullName }}/pulls/{{ $pull.PullId }}" class="no-underline hover:no-underline"> 22 + <div class="flex text-sm items-center gap-2"> 23 + {{ block "summarizedPullState" $pull }} {{ end }} 24 + <span class="dark:text-white"> 25 + <span class="text-gray-500 dark:text-gray-400">#{{ $pull.PullId }}</span> 26 + {{ $pull.Title }} 27 + </span> 28 + </div> 29 + </a> 30 + {{ end }} 31 + 32 + {{ define "summarizedPullState" }} 33 + {{ $fgColor := "text-gray-600 dark:text-gray-300" }} 34 + {{ $icon := "ban" }} 35 + 36 + {{ if .State.IsOpen }} 37 + {{ $fgColor = "text-green-600 dark:text-green-500" }} 38 + {{ $icon = "git-pull-request" }} 39 + {{ else if .State.IsMerged }} 40 + {{ $fgColor = "text-purple-600 dark:text-purple-500" }} 41 + {{ $icon = "git-merge" }} 42 + {{ end }} 43 + 44 + {{ $style := printf "w-4 h-4 %s" $fgColor }} 45 + 46 + {{ i $icon $style }} 47 + {{ end }}
+8 -1
appview/pages/templates/repo/pulls/pull.html
··· 11 11 12 12 13 13 {{ define "repoContent" }} 14 - {{ template "repo/pulls/fragments/pullHeader" . }} 14 + {{ template "repo/pulls/fragments/pullHeader" . }} 15 + 16 + {{ if .Pull.IsStacked }} 17 + <div class="mt-8"> 18 + <p class="text-sm font-bold p-2 dark:text-white">STACK</p> 19 + {{ template "repo/pulls/fragments/pullStack" . }} 20 + </div> 21 + {{ end }} 15 22 {{ end }} 16 23 17 24 {{ define "repoAfter" }}
+11 -3
appview/state/pull.go
··· 136 136 RepoInfo: f.RepoInfo(s, user), 137 137 DidHandleMap: didHandleMap, 138 138 Pull: pull, 139 + Stack: stack, 139 140 MergeCheck: mergeCheckResponse, 140 141 ResubmitCheck: resubmitResult, 141 142 }) ··· 303 304 return 304 305 } 305 306 307 + stack, _ := r.Context().Value("stack").(db.Stack) 308 + 306 309 roundId := chi.URLParam(r, "round") 307 310 roundIdInt, err := strconv.Atoi(roundId) 308 311 if err != nil || roundIdInt >= len(pull.Submissions) { ··· 329 332 DidHandleMap: didHandleMap, 330 333 RepoInfo: f.RepoInfo(s, user), 331 334 Pull: pull, 335 + Stack: stack, 332 336 Round: roundIdInt, 333 337 Submission: pull.Submissions[roundIdInt], 334 338 Diff: &diff, ··· 1647 1651 if op, ok := origById[np.ChangeId]; ok { 1648 1652 // pull exists in both stacks 1649 1653 // TODO: can we avoid reparse? 1650 - origFiles, _, _ := gitdiff.Parse(strings.NewReader(op.LatestPatch())) 1651 - newFiles, _, _ := gitdiff.Parse(strings.NewReader(np.LatestPatch())) 1654 + origFiles, origHeaderStr, _ := gitdiff.Parse(strings.NewReader(op.LatestPatch())) 1655 + newFiles, newHeaderStr, _ := gitdiff.Parse(strings.NewReader(np.LatestPatch())) 1656 + 1657 + origHeader, _ := gitdiff.ParsePatchHeader(origHeaderStr) 1658 + newHeader, _ := gitdiff.ParsePatchHeader(newHeaderStr) 1652 1659 1653 1660 patchutil.SortPatch(newFiles) 1654 1661 patchutil.SortPatch(origFiles) 1655 1662 1656 - if patchutil.Equal(newFiles, origFiles) { 1663 + // text content of patch may be identical, but a jj rebase might have forwarded it 1664 + if patchutil.Equal(newFiles, origFiles) && origHeader.SHA == newHeader.SHA { 1657 1665 unchanged[op.ChangeId] = struct{}{} 1658 1666 } else { 1659 1667 updated[op.ChangeId] = struct{}{}