git-pr / feat: consistent user display #7

accepted · opened on 2024-07-19T20:02:10Z by jolheiser
Help
# add changes to patch request
git format-patch main --stdout | ssh pr.pico.sh pr add 7
# add review to patch request
git format-patch main --stdout | ssh pr.pico.sh pr add --review 7
# remove patchset
ssh pr.pico.sh ps rm ps-x
# checkout all patches
ssh pr.pico.sh pr print 7 | git am -3
# print a diff between the last two patches in a patch request
ssh pr.pico.sh pr diff 7
# accept PR
ssh pr.pico.sh pr accept 7
# close PR
ssh pr.pico.sh pr close 7

Logs

jolheiser created pr with ps-10 on 2024-07-19T20:02:10Z
erock reviewed pr with ps-11 on 2024-07-20T00:28:08Z
erock changed status on 2024-07-20T00:28:08Z {"status":"reviewed"}
erock reviewed pr with ps-12 on 2024-07-20T00:33:12Z
erock changed status on 2024-07-20T00:33:12Z {"status":"reviewed"}
erock reviewed pr with ps-13 on 2024-07-20T00:38:03Z
erock changed status on 2024-07-20T00:38:03Z {"status":"reviewed"}
erock changed status on 2024-07-20T01:34:54Z {"status":"accepted"}

Patchsets

Diff ↕

feat: consistent user display

jolheiser <git@jolheiser.com>
This patch unifies how user accounts look across the entire frontend
It also adds some CSS specifically for reviews/admins

Signed-off-by: jolheiser <git@jolheiser.com>
 static/git-pr.css      |  20 +++++++++
 static/vars.css        |   2 +
 tmpl/base.html         |   1 +
 tmpl/patch.html        |   4 +-
 tmpl/pr-detail.html    |  12 ++---
 tmpl/pr-header.html    |   2 +-
 tmpl/pr-list-item.html |   2 +-
 tmpl/pr-status.html    |   2 +-
 web.go                 | 100 ++++++++++++++++++++++++++---------------
 9 files changed, 97 insertions(+), 48 deletions(-)
 create mode 100644 static/git-pr.css
  1From e03fb019f21a6b9648b5bf5def43178450aade0f Mon Sep 17 00:00:00 2001
  2From: jolheiser <git@jolheiser.com>
  3Date: Fri, 19 Jul 2024 11:26:38 -0500
  4Subject: [PATCH] feat: consistent user display
  5
  6This patch unifies how user accounts look across the entire frontend
  7It also adds some CSS specifically for reviews/admins
  8
  9Signed-off-by: jolheiser <git@jolheiser.com>
 10---
 11 static/git-pr.css      |  20 +++++++++
 12 static/vars.css        |   2 +
 13 tmpl/base.html         |   1 +
 14 tmpl/patch.html        |   4 +-
 15 tmpl/pr-detail.html    |  12 ++---
 16 tmpl/pr-header.html    |   2 +-
 17 tmpl/pr-list-item.html |   2 +-
 18 tmpl/pr-status.html    |   2 +-
 19 web.go                 | 100 ++++++++++++++++++++++++++---------------
 20 9 files changed, 97 insertions(+), 48 deletions(-)
 21 create mode 100644 static/git-pr.css
 22
 23diff --git a/static/git-pr.css b/static/git-pr.css
 24new file mode 100644
 25index 0000000..fe6decf
 26--- /dev/null
 27+++ b/static/git-pr.css
 28@@ -0,0 +1,20 @@
 29+.pill-review {
 30+  border: 1px solid var(--review);
 31+  color: var(--review);
 32+}
 33+
 34+.box-sm-review {
 35+  border: 2px solid var(--review);
 36+  padding: 0.15rem 0.35rem;
 37+}
 38+
 39+.box-review {
 40+  border: 2px solid var(--review);
 41+  padding: 0.5rem 0.75rem;
 42+}
 43+
 44+.pill-admin {
 45+  border: 1px solid var(--admin);
 46+  color: var(--admin);
 47+}
 48+
 49diff --git a/static/vars.css b/static/vars.css
 50index 6300534..a1aae4a 100644
 51--- a/static/vars.css
 52+++ b/static/vars.css
 53@@ -15,4 +15,6 @@
 54   --grey: #414558;
 55   --grey-light: #6a708e;
 56   --shadow: #252525;
 57+  --review: #f9e2af;
 58+  --admin: #f38ba8;
 59 }
 60diff --git a/tmpl/base.html b/tmpl/base.html
 61index b5f5fb3..c7daa8d 100644
 62--- a/tmpl/base.html
 63+++ b/tmpl/base.html
 64@@ -10,6 +10,7 @@
 65     {{template "meta" .}}
 66 
 67     <link rel="stylesheet" href="/static/smol.css" />
 68+    <link rel="stylesheet" href="/static/git-pr.css" />
 69     <link rel="stylesheet" href="/static/vars.css" />
 70     <link rel="stylesheet" href="/syntax.css" />
 71   </head>
 72diff --git a/tmpl/patch.html b/tmpl/patch.html
 73index d7232d9..2a64344 100644
 74--- a/tmpl/patch.html
 75+++ b/tmpl/patch.html
 76@@ -1,12 +1,12 @@
 77 {{define "patch"}}
 78 <div>
 79   <h3 class="text-lg m-0 p-0 mb">
 80-    {{if .Review}}<code class="pill-alert">REVIEW</code>{{end}}
 81+    {{if .Review}}<code class="pill-review">REVIEW</code>{{end}}
 82     <a href="#{{.Url}}">{{.Title}}</a>
 83   </h3>
 84 
 85   <div class="group-h text-sm">
 86-    <code class="pill{{if .Review}}-alert{{end}}">{{.AuthorName}} &lt;{{.AuthorEmail}}&gt;</code>
 87+    <code class="pill{{if .Review}}-review{{end}}">{{.AuthorName}} &lt;{{.AuthorEmail}}&gt;</code>
 88     <date>{{.FormattedAuthorDate}}</date>
 89   </div>
 90 </div>
 91diff --git a/tmpl/pr-detail.html b/tmpl/pr-detail.html
 92index f889c8b..0c59ade 100644
 93--- a/tmpl/pr-detail.html
 94+++ b/tmpl/pr-detail.html
 95@@ -23,14 +23,14 @@
 96 
 97     {{range .Logs}}
 98     <div>
 99-      <code class='pill{{if eq .Event "pr_reviewed"}}-alert{{end}}'>{{.UserName}}</code>
100+      <code class='pill{{if .UserData.IsAdmin}}-admin{{end}}' title="{{.UserData.Pubkey}}">{{.UserData.Name}}</code>
101       <span class="font-bold">
102         {{if eq .Event "pr_created"}}
103           created pr with <code>{{.FormattedPatchsetID}}</code>
104         {{else if eq .Event "pr_patchset_added"}}
105           added <code>{{.FormattedPatchsetID}}</code>
106         {{else if eq .Event "pr_reviewed"}}
107-          reviewed pr with <code class="pill-alert">{{.FormattedPatchsetID}}</code>
108+          reviewed pr with <code class="pill-review">{{.FormattedPatchsetID}}</code>
109         {{else if eq .Event "pr_patchset_replaced"}}
110           replaced <code>{{.FormattedPatchsetID}}</code>
111         {{else if eq .Event "pr_status_changed"}}
112@@ -55,7 +55,7 @@
113         <summary class="text-sm">Diff ↕</summary>
114         <div class="group">
115           {{range .DiffPatches}}
116-            <div class="box-sm{{if .Review}}-alert{{end}} group" id="{{.Url}}">
117+            <div class="box-sm{{if .Review}}-review{{end}} group" id="{{.Url}}">
118               {{template "patch" .}}
119             </div>
120           {{else}}
121@@ -65,9 +65,9 @@
122       </details>
123 
124       <div>
125-        <code class="{{if .Review}}pill-alert{{end}}">{{.FormattedID}}</code>
126+        <code class="{{if .Review}}pill-review{{end}}">{{.FormattedID}}</code>
127         <span> by </span>
128-        <code class="pill{{if .Review}}-alert{{end}}">{{.UserName}}</code>
129+        <code class="pill{{if .UserData.IsAdmin}}-admin{{end}}" title="{{.UserData.Pubkey}}">{{.UserData.Name}}</code>
130         <span>on <date>{{.Date}}</date></span>
131       </div>
132     {{end}}
133@@ -77,7 +77,7 @@
134 
135   <div class="group">
136     {{range $idx, $val := .Patches}}
137-      <div class="box{{if .Review}}-alert{{end}} group" id="{{.Url}}">
138+      <div class="box{{if .Review}}-review{{end}} group" id="{{.Url}}">
139         {{template "patch" .}}
140       </div>
141     {{else}}
142diff --git a/tmpl/pr-header.html b/tmpl/pr-header.html
143index df856c0..8ed1789 100644
144--- a/tmpl/pr-header.html
145+++ b/tmpl/pr-header.html
146@@ -8,7 +8,7 @@
147   <div class="text-sm">
148     {{template "pr-status" .Pr.Status}}
149     <span>opened on <date>{{.Pr.Date}}</date> by</span>
150-    <code class="{{if .Pr.IsAdmin}}pill-alert{{end}}" title="{{.Pr.Pubkey}}">{{.Pr.UserName}}</code>
151+    <code class="pill{{if .Pr.UserData.IsAdmin}}-admin{{end}}" title="{{.Pr.UserData.Pubkey}}">{{.Pr.UserData.Name}}</code>
152   </div>
153 
154   <details>
155diff --git a/tmpl/pr-list-item.html b/tmpl/pr-list-item.html
156index 5a0ce02..f46f117 100644
157--- a/tmpl/pr-list-item.html
158+++ b/tmpl/pr-list-item.html
159@@ -7,7 +7,7 @@
160   <div>
161     <code>#{{.ID}}</code>
162     <span>opened on <date>{{.Date}}</date> by </span>
163-    <code class="{{if .IsAdmin}}pill-alert{{end}}" title="{{.Pubkey}}">{{.UserName}}</code>
164+    <code class="{{if .UserData.IsAdmin}}pill-admin{{end}}" title="{{.UserData.Pubkey}}">{{.UserData.Name}}</code>
165   </div>
166 </div>
167 {{end}}
168diff --git a/tmpl/pr-status.html b/tmpl/pr-status.html
169index 37eef5d..2021c00 100644
170--- a/tmpl/pr-status.html
171+++ b/tmpl/pr-status.html
172@@ -1,6 +1,6 @@
173 {{define "pr-status"}}
174   {{if eq . "reviewed"}}
175-    <code class="pill-alert">{{.}}</code>
176+    <code class="pill-review">{{.}}</code>
177   {{else if eq . "opened"}}
178     <code class="pill-info">{{.}}</code>
179   {{else if eq . "accepted"}}
180diff --git a/web.go b/web.go
181index 9419dea..2e9fed5 100644
182--- a/web.go
183+++ b/web.go
184@@ -130,10 +130,12 @@ func repoListHandler(w http.ResponseWriter, r *http.Request) {
185 			}
186 			isAdmin := web.Backend.IsAdmin(pk)
187 			ls = &PrListData{
188-				ID:       curpr.ID,
189-				IsAdmin:  isAdmin,
190-				UserName: repo.User.Name,
191-				Pubkey:   repo.User.Pubkey,
192+				ID: curpr.ID,
193+				UserData: UserData{
194+					Name:    repo.User.Name,
195+					IsAdmin: isAdmin,
196+					Pubkey:  repo.User.Pubkey,
197+				},
198 				LinkData: LinkData{
199 					Url:  template.URL(fmt.Sprintf("/prs/%d", curpr.ID)),
200 					Text: curpr.Name,
201@@ -167,18 +169,22 @@ func repoListHandler(w http.ResponseWriter, r *http.Request) {
202 	}
203 }
204 
205+type UserData struct {
206+	Name    string
207+	IsAdmin bool
208+	Pubkey  string
209+}
210+
211 type MetaData struct {
212 	URL string
213 }
214 
215 type PrListData struct {
216 	LinkData
217-	ID       int64
218-	IsAdmin  bool
219-	UserName string
220-	Pubkey   string
221-	Date     string
222-	Status   string
223+	UserData
224+	ID     int64
225+	Date   string
226+	Status string
227 }
228 
229 type RepoDetailData struct {
230@@ -233,10 +239,12 @@ func repoDetailHandler(w http.ResponseWriter, r *http.Request) {
231 		}
232 		isAdmin := web.Backend.IsAdmin(pk)
233 		ls := PrListData{
234-			ID:       curpr.ID,
235-			IsAdmin:  isAdmin,
236-			UserName: user.Name,
237-			Pubkey:   user.Pubkey,
238+			ID: curpr.ID,
239+			UserData: UserData{
240+				Name:    user.Name,
241+				IsAdmin: isAdmin,
242+				Pubkey:  user.Pubkey,
243+			},
244 			LinkData: LinkData{
245 				Url:  template.URL(fmt.Sprintf("/prs/%d", curpr.ID)),
246 				Text: curpr.Name,
247@@ -276,13 +284,11 @@ func repoDetailHandler(w http.ResponseWriter, r *http.Request) {
248 }
249 
250 type PrData struct {
251-	ID       int64
252-	IsAdmin  bool
253-	Title    string
254-	Date     string
255-	UserName string
256-	Pubkey   string
257-	Status   string
258+	UserData
259+	ID     int64
260+	Title  string
261+	Date   string
262+	Status string
263 }
264 
265 type PatchData struct {
266@@ -295,17 +301,15 @@ type PatchData struct {
267 
268 type EventLogData struct {
269 	*EventLog
270+	UserData
271 	FormattedPatchsetID string
272-	UserName            string
273-	Pubkey              string
274 	Date                string
275 }
276 
277 type PatchsetData struct {
278 	*Patchset
279+	UserData
280 	FormattedID string
281-	UserName    string
282-	Pubkey      string
283 	Date        string
284 	DiffPatches []PatchData
285 }
286@@ -392,11 +396,21 @@ func prDetailHandler(w http.ResponseWriter, r *http.Request) {
287 			})
288 		}
289 
290+		pk, err := web.Backend.PubkeyToPublicKey(user.Pubkey)
291+		if err != nil {
292+			web.Logger.Error("cannot parse pubkey for pr user", "err", err)
293+			w.WriteHeader(http.StatusUnprocessableEntity)
294+			return
295+		}
296+
297 		patchsetsData = append(patchsetsData, PatchsetData{
298 			Patchset:    patchset,
299 			FormattedID: getFormattedPatchsetID(patchset.ID),
300-			UserName:    user.Name,
301-			Pubkey:      user.Pubkey,
302+			UserData: UserData{
303+				Name:    user.Name,
304+				IsAdmin: web.Backend.IsAdmin(pk),
305+				Pubkey:  user.Pubkey,
306+			},
307 			Date:        patchset.CreatedAt.Format(time.RFC3339),
308 			DiffPatches: patchesData,
309 		})
310@@ -469,12 +483,22 @@ func prDetailHandler(w http.ResponseWriter, r *http.Request) {
311 	logData := []EventLogData{}
312 	for _, eventlog := range logs {
313 		user, _ := web.Pr.GetUserByID(eventlog.UserID)
314+		pk, err := web.Backend.PubkeyToPublicKey(user.Pubkey)
315+		if err != nil {
316+			web.Logger.Error("cannot parse pubkey for pr user", "err", err)
317+			w.WriteHeader(http.StatusUnprocessableEntity)
318+			return
319+		}
320+
321 		logData = append(logData, EventLogData{
322 			EventLog:            eventlog,
323 			FormattedPatchsetID: getFormattedPatchsetID(eventlog.PatchsetID.Int64),
324-			UserName:            user.Name,
325-			Pubkey:              user.Pubkey,
326-			Date:                pr.CreatedAt.Format(web.Backend.Cfg.TimeFormat),
327+			UserData: UserData{
328+				Name:    user.Name,
329+				IsAdmin: web.Backend.IsAdmin(pk),
330+				Pubkey:  user.Pubkey,
331+			},
332+			Date: pr.CreatedAt.Format(web.Backend.Cfg.TimeFormat),
333 		})
334 	}
335 
336@@ -489,13 +513,15 @@ func prDetailHandler(w http.ResponseWriter, r *http.Request) {
337 		Patchsets: patchsetsData,
338 		Logs:      logData,
339 		Pr: PrData{
340-			ID:       pr.ID,
341-			IsAdmin:  isAdmin,
342-			Title:    pr.Name,
343-			UserName: user.Name,
344-			Pubkey:   user.Pubkey,
345-			Date:     pr.CreatedAt.Format(web.Backend.Cfg.TimeFormat),
346-			Status:   pr.Status,
347+			ID: pr.ID,
348+			UserData: UserData{
349+				Name:    user.Name,
350+				IsAdmin: isAdmin,
351+				Pubkey:  user.Pubkey,
352+			},
353+			Title:  pr.Name,
354+			Date:   pr.CreatedAt.Format(web.Backend.Cfg.TimeFormat),
355+			Status: pr.Status,
356 		},
357 		MetaData: MetaData{
358 			URL: web.Backend.Cfg.Url,
359-- 
3602.45.1
361
ps-10 by jolheiser on 2024-07-19T20:02:10Z
Diff ↕

REVIEW Overall looks good to me!

Eric Bower <me@erock.io>
The colors look good, it does make me wonder about the differences
between an admin and a reviewer since they are almost always the same.

I guess it's nice to be able to flag a patch as a review.

jolheiser (1):
  feat: consistent user display

 static/git-pr.css      |  20 +++++++++
 static/vars.css        |   2 +
 tmpl/base.html         |   1 +
 tmpl/patch.html        |   4 +-
 tmpl/pr-detail.html    |  12 ++---
 tmpl/pr-header.html    |   2 +-
 tmpl/pr-list-item.html |   2 +-
 tmpl/pr-status.html    |   2 +-
 web.go                 | 100 ++++++++++++++++++++++++++---------------
 9 files changed, 97 insertions(+), 48 deletions(-)
 create mode 100644 static/git-pr.css

base-commit: affd3a6ff2e1754bc5bcedeca5dd686aeffe5d5a
--
2.45.2
 1From aba80b9e3e3b9eae2bd6e551c60a36aa81aa23d8 Mon Sep 17 00:00:00 2001
 2From: Eric Bower <me@erock.io>
 3Date: Fri, 19 Jul 2024 20:31:18 -0400
 4Subject: [PATCH 0/1] Overall looks good to me!
 5
 6The colors look good, it does make me wonder about the differences
 7between an admin and a reviewer since they are almost always the same.
 8
 9I guess it's nice to be able to flag a patch as a review.
10
11jolheiser (1):
12  feat: consistent user display
13
14 static/git-pr.css      |  20 +++++++++
15 static/vars.css        |   2 +
16 tmpl/base.html         |   1 +
17 tmpl/patch.html        |   4 +-
18 tmpl/pr-detail.html    |  12 ++---
19 tmpl/pr-header.html    |   2 +-
20 tmpl/pr-list-item.html |   2 +-
21 tmpl/pr-status.html    |   2 +-
22 web.go                 | 100 ++++++++++++++++++++++++++---------------
23 9 files changed, 97 insertions(+), 48 deletions(-)
24 create mode 100644 static/git-pr.css
25
26
27base-commit: affd3a6ff2e1754bc5bcedeca5dd686aeffe5d5a
28-- 
292.45.2
ps-12 by erock on 2024-07-20T00:33:12Z
Diff ↕
No patches found, that doesn't seem right.
ps-13 by erock on 2024-07-20T00:38:03Z

feat: consistent user display

jolheiser <git@jolheiser.com> 2024-07-19T16:26:38Z
This patch unifies how user accounts look across the entire frontend
It also adds some CSS specifically for reviews/admins

Signed-off-by: jolheiser <git@jolheiser.com>
Notes:
    review: looks good!

    I like the colors, but it does make me wonder about the difference
    between a reviewer and an admin since they are the same atm.

 static/git-pr.css      |  20 +++++++++
 static/vars.css        |   2 +
 tmpl/base.html         |   1 +
 tmpl/patch.html        |   4 +-
 tmpl/pr-detail.html    |  12 ++---
 tmpl/pr-header.html    |   2 +-
 tmpl/pr-list-item.html |   2 +-
 tmpl/pr-status.html    |   2 +-
 web.go                 | 100 ++++++++++++++++++++++++++---------------
 9 files changed, 97 insertions(+), 48 deletions(-)
 create mode 100644 static/git-pr.css
  1From aba80b9e3e3b9eae2bd6e551c60a36aa81aa23d8 Mon Sep 17 00:00:00 2001
  2From: jolheiser <git@jolheiser.com>
  3Date: Fri, 19 Jul 2024 11:26:38 -0500
  4Subject: [PATCH] feat: consistent user display
  5
  6This patch unifies how user accounts look across the entire frontend
  7It also adds some CSS specifically for reviews/admins
  8
  9Signed-off-by: jolheiser <git@jolheiser.com>
 10---
 11
 12Notes:
 13    review: looks good!
 14    
 15    I like the colors, but it does make me wonder about the difference
 16    between a reviewer and an admin since they are the same atm.
 17
 18 static/git-pr.css      |  20 +++++++++
 19 static/vars.css        |   2 +
 20 tmpl/base.html         |   1 +
 21 tmpl/patch.html        |   4 +-
 22 tmpl/pr-detail.html    |  12 ++---
 23 tmpl/pr-header.html    |   2 +-
 24 tmpl/pr-list-item.html |   2 +-
 25 tmpl/pr-status.html    |   2 +-
 26 web.go                 | 100 ++++++++++++++++++++++++++---------------
 27 9 files changed, 97 insertions(+), 48 deletions(-)
 28 create mode 100644 static/git-pr.css
 29
 30diff --git a/static/git-pr.css b/static/git-pr.css
 31new file mode 100644
 32index 0000000..fe6decf
 33--- /dev/null
 34+++ b/static/git-pr.css
 35@@ -0,0 +1,20 @@
 36+.pill-review {
 37+  border: 1px solid var(--review);
 38+  color: var(--review);
 39+}
 40+
 41+.box-sm-review {
 42+  border: 2px solid var(--review);
 43+  padding: 0.15rem 0.35rem;
 44+}
 45+
 46+.box-review {
 47+  border: 2px solid var(--review);
 48+  padding: 0.5rem 0.75rem;
 49+}
 50+
 51+.pill-admin {
 52+  border: 1px solid var(--admin);
 53+  color: var(--admin);
 54+}
 55+
 56diff --git a/static/vars.css b/static/vars.css
 57index 6300534..a1aae4a 100644
 58--- a/static/vars.css
 59+++ b/static/vars.css
 60@@ -15,4 +15,6 @@
 61   --grey: #414558;
 62   --grey-light: #6a708e;
 63   --shadow: #252525;
 64+  --review: #f9e2af;
 65+  --admin: #f38ba8;
 66 }
 67diff --git a/tmpl/base.html b/tmpl/base.html
 68index b5f5fb3..c7daa8d 100644
 69--- a/tmpl/base.html
 70+++ b/tmpl/base.html
 71@@ -10,6 +10,7 @@
 72     {{template "meta" .}}
 73 
 74     <link rel="stylesheet" href="/static/smol.css" />
 75+    <link rel="stylesheet" href="/static/git-pr.css" />
 76     <link rel="stylesheet" href="/static/vars.css" />
 77     <link rel="stylesheet" href="/syntax.css" />
 78   </head>
 79diff --git a/tmpl/patch.html b/tmpl/patch.html
 80index d7232d9..2a64344 100644
 81--- a/tmpl/patch.html
 82+++ b/tmpl/patch.html
 83@@ -1,12 +1,12 @@
 84 {{define "patch"}}
 85 <div>
 86   <h3 class="text-lg m-0 p-0 mb">
 87-    {{if .Review}}<code class="pill-alert">REVIEW</code>{{end}}
 88+    {{if .Review}}<code class="pill-review">REVIEW</code>{{end}}
 89     <a href="#{{.Url}}">{{.Title}}</a>
 90   </h3>
 91 
 92   <div class="group-h text-sm">
 93-    <code class="pill{{if .Review}}-alert{{end}}">{{.AuthorName}} &lt;{{.AuthorEmail}}&gt;</code>
 94+    <code class="pill{{if .Review}}-review{{end}}">{{.AuthorName}} &lt;{{.AuthorEmail}}&gt;</code>
 95     <date>{{.FormattedAuthorDate}}</date>
 96   </div>
 97 </div>
 98diff --git a/tmpl/pr-detail.html b/tmpl/pr-detail.html
 99index f889c8b..0c59ade 100644
100--- a/tmpl/pr-detail.html
101+++ b/tmpl/pr-detail.html
102@@ -23,14 +23,14 @@
103 
104     {{range .Logs}}
105     <div>
106-      <code class='pill{{if eq .Event "pr_reviewed"}}-alert{{end}}'>{{.UserName}}</code>
107+      <code class='pill{{if .UserData.IsAdmin}}-admin{{end}}' title="{{.UserData.Pubkey}}">{{.UserData.Name}}</code>
108       <span class="font-bold">
109         {{if eq .Event "pr_created"}}
110           created pr with <code>{{.FormattedPatchsetID}}</code>
111         {{else if eq .Event "pr_patchset_added"}}
112           added <code>{{.FormattedPatchsetID}}</code>
113         {{else if eq .Event "pr_reviewed"}}
114-          reviewed pr with <code class="pill-alert">{{.FormattedPatchsetID}}</code>
115+          reviewed pr with <code class="pill-review">{{.FormattedPatchsetID}}</code>
116         {{else if eq .Event "pr_patchset_replaced"}}
117           replaced <code>{{.FormattedPatchsetID}}</code>
118         {{else if eq .Event "pr_status_changed"}}
119@@ -55,7 +55,7 @@
120         <summary class="text-sm">Diff ↕</summary>
121         <div class="group">
122           {{range .DiffPatches}}
123-            <div class="box-sm{{if .Review}}-alert{{end}} group" id="{{.Url}}">
124+            <div class="box-sm{{if .Review}}-review{{end}} group" id="{{.Url}}">
125               {{template "patch" .}}
126             </div>
127           {{else}}
128@@ -65,9 +65,9 @@
129       </details>
130 
131       <div>
132-        <code class="{{if .Review}}pill-alert{{end}}">{{.FormattedID}}</code>
133+        <code class="{{if .Review}}pill-review{{end}}">{{.FormattedID}}</code>
134         <span> by </span>
135-        <code class="pill{{if .Review}}-alert{{end}}">{{.UserName}}</code>
136+        <code class="pill{{if .UserData.IsAdmin}}-admin{{end}}" title="{{.UserData.Pubkey}}">{{.UserData.Name}}</code>
137         <span>on <date>{{.Date}}</date></span>
138       </div>
139     {{end}}
140@@ -77,7 +77,7 @@
141 
142   <div class="group">
143     {{range $idx, $val := .Patches}}
144-      <div class="box{{if .Review}}-alert{{end}} group" id="{{.Url}}">
145+      <div class="box{{if .Review}}-review{{end}} group" id="{{.Url}}">
146         {{template "patch" .}}
147       </div>
148     {{else}}
149diff --git a/tmpl/pr-header.html b/tmpl/pr-header.html
150index df856c0..8ed1789 100644
151--- a/tmpl/pr-header.html
152+++ b/tmpl/pr-header.html
153@@ -8,7 +8,7 @@
154   <div class="text-sm">
155     {{template "pr-status" .Pr.Status}}
156     <span>opened on <date>{{.Pr.Date}}</date> by</span>
157-    <code class="{{if .Pr.IsAdmin}}pill-alert{{end}}" title="{{.Pr.Pubkey}}">{{.Pr.UserName}}</code>
158+    <code class="pill{{if .Pr.UserData.IsAdmin}}-admin{{end}}" title="{{.Pr.UserData.Pubkey}}">{{.Pr.UserData.Name}}</code>
159   </div>
160 
161   <details>
162diff --git a/tmpl/pr-list-item.html b/tmpl/pr-list-item.html
163index 5a0ce02..f46f117 100644
164--- a/tmpl/pr-list-item.html
165+++ b/tmpl/pr-list-item.html
166@@ -7,7 +7,7 @@
167   <div>
168     <code>#{{.ID}}</code>
169     <span>opened on <date>{{.Date}}</date> by </span>
170-    <code class="{{if .IsAdmin}}pill-alert{{end}}" title="{{.Pubkey}}">{{.UserName}}</code>
171+    <code class="{{if .UserData.IsAdmin}}pill-admin{{end}}" title="{{.UserData.Pubkey}}">{{.UserData.Name}}</code>
172   </div>
173 </div>
174 {{end}}
175diff --git a/tmpl/pr-status.html b/tmpl/pr-status.html
176index 37eef5d..2021c00 100644
177--- a/tmpl/pr-status.html
178+++ b/tmpl/pr-status.html
179@@ -1,6 +1,6 @@
180 {{define "pr-status"}}
181   {{if eq . "reviewed"}}
182-    <code class="pill-alert">{{.}}</code>
183+    <code class="pill-review">{{.}}</code>
184   {{else if eq . "opened"}}
185     <code class="pill-info">{{.}}</code>
186   {{else if eq . "accepted"}}
187diff --git a/web.go b/web.go
188index 9419dea..2e9fed5 100644
189--- a/web.go
190+++ b/web.go
191@@ -130,10 +130,12 @@ func repoListHandler(w http.ResponseWriter, r *http.Request) {
192 			}
193 			isAdmin := web.Backend.IsAdmin(pk)
194 			ls = &PrListData{
195-				ID:       curpr.ID,
196-				IsAdmin:  isAdmin,
197-				UserName: repo.User.Name,
198-				Pubkey:   repo.User.Pubkey,
199+				ID: curpr.ID,
200+				UserData: UserData{
201+					Name:    repo.User.Name,
202+					IsAdmin: isAdmin,
203+					Pubkey:  repo.User.Pubkey,
204+				},
205 				LinkData: LinkData{
206 					Url:  template.URL(fmt.Sprintf("/prs/%d", curpr.ID)),
207 					Text: curpr.Name,
208@@ -167,18 +169,22 @@ func repoListHandler(w http.ResponseWriter, r *http.Request) {
209 	}
210 }
211 
212+type UserData struct {
213+	Name    string
214+	IsAdmin bool
215+	Pubkey  string
216+}
217+
218 type MetaData struct {
219 	URL string
220 }
221 
222 type PrListData struct {
223 	LinkData
224-	ID       int64
225-	IsAdmin  bool
226-	UserName string
227-	Pubkey   string
228-	Date     string
229-	Status   string
230+	UserData
231+	ID     int64
232+	Date   string
233+	Status string
234 }
235 
236 type RepoDetailData struct {
237@@ -233,10 +239,12 @@ func repoDetailHandler(w http.ResponseWriter, r *http.Request) {
238 		}
239 		isAdmin := web.Backend.IsAdmin(pk)
240 		ls := PrListData{
241-			ID:       curpr.ID,
242-			IsAdmin:  isAdmin,
243-			UserName: user.Name,
244-			Pubkey:   user.Pubkey,
245+			ID: curpr.ID,
246+			UserData: UserData{
247+				Name:    user.Name,
248+				IsAdmin: isAdmin,
249+				Pubkey:  user.Pubkey,
250+			},
251 			LinkData: LinkData{
252 				Url:  template.URL(fmt.Sprintf("/prs/%d", curpr.ID)),
253 				Text: curpr.Name,
254@@ -276,13 +284,11 @@ func repoDetailHandler(w http.ResponseWriter, r *http.Request) {
255 }
256 
257 type PrData struct {
258-	ID       int64
259-	IsAdmin  bool
260-	Title    string
261-	Date     string
262-	UserName string
263-	Pubkey   string
264-	Status   string
265+	UserData
266+	ID     int64
267+	Title  string
268+	Date   string
269+	Status string
270 }
271 
272 type PatchData struct {
273@@ -295,17 +301,15 @@ type PatchData struct {
274 
275 type EventLogData struct {
276 	*EventLog
277+	UserData
278 	FormattedPatchsetID string
279-	UserName            string
280-	Pubkey              string
281 	Date                string
282 }
283 
284 type PatchsetData struct {
285 	*Patchset
286+	UserData
287 	FormattedID string
288-	UserName    string
289-	Pubkey      string
290 	Date        string
291 	DiffPatches []PatchData
292 }
293@@ -392,11 +396,21 @@ func prDetailHandler(w http.ResponseWriter, r *http.Request) {
294 			})
295 		}
296 
297+		pk, err := web.Backend.PubkeyToPublicKey(user.Pubkey)
298+		if err != nil {
299+			web.Logger.Error("cannot parse pubkey for pr user", "err", err)
300+			w.WriteHeader(http.StatusUnprocessableEntity)
301+			return
302+		}
303+
304 		patchsetsData = append(patchsetsData, PatchsetData{
305 			Patchset:    patchset,
306 			FormattedID: getFormattedPatchsetID(patchset.ID),
307-			UserName:    user.Name,
308-			Pubkey:      user.Pubkey,
309+			UserData: UserData{
310+				Name:    user.Name,
311+				IsAdmin: web.Backend.IsAdmin(pk),
312+				Pubkey:  user.Pubkey,
313+			},
314 			Date:        patchset.CreatedAt.Format(time.RFC3339),
315 			DiffPatches: patchesData,
316 		})
317@@ -469,12 +483,22 @@ func prDetailHandler(w http.ResponseWriter, r *http.Request) {
318 	logData := []EventLogData{}
319 	for _, eventlog := range logs {
320 		user, _ := web.Pr.GetUserByID(eventlog.UserID)
321+		pk, err := web.Backend.PubkeyToPublicKey(user.Pubkey)
322+		if err != nil {
323+			web.Logger.Error("cannot parse pubkey for pr user", "err", err)
324+			w.WriteHeader(http.StatusUnprocessableEntity)
325+			return
326+		}
327+
328 		logData = append(logData, EventLogData{
329 			EventLog:            eventlog,
330 			FormattedPatchsetID: getFormattedPatchsetID(eventlog.PatchsetID.Int64),
331-			UserName:            user.Name,
332-			Pubkey:              user.Pubkey,
333-			Date:                pr.CreatedAt.Format(web.Backend.Cfg.TimeFormat),
334+			UserData: UserData{
335+				Name:    user.Name,
336+				IsAdmin: web.Backend.IsAdmin(pk),
337+				Pubkey:  user.Pubkey,
338+			},
339+			Date: pr.CreatedAt.Format(web.Backend.Cfg.TimeFormat),
340 		})
341 	}
342 
343@@ -489,13 +513,15 @@ func prDetailHandler(w http.ResponseWriter, r *http.Request) {
344 		Patchsets: patchsetsData,
345 		Logs:      logData,
346 		Pr: PrData{
347-			ID:       pr.ID,
348-			IsAdmin:  isAdmin,
349-			Title:    pr.Name,
350-			UserName: user.Name,
351-			Pubkey:   user.Pubkey,
352-			Date:     pr.CreatedAt.Format(web.Backend.Cfg.TimeFormat),
353-			Status:   pr.Status,
354+			ID: pr.ID,
355+			UserData: UserData{
356+				Name:    user.Name,
357+				IsAdmin: isAdmin,
358+				Pubkey:  user.Pubkey,
359+			},
360+			Title:  pr.Name,
361+			Date:   pr.CreatedAt.Format(web.Backend.Cfg.TimeFormat),
362+			Status: pr.Status,
363 		},
364 		MetaData: MetaData{
365 			URL: web.Backend.Cfg.Url,
366
367base-commit: affd3a6ff2e1754bc5bcedeca5dd686aeffe5d5a
368-- 
3692.45.2
370