From b7df865e661cd165baf7b14ca7db7f6b6a63c7c3 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Fri, 10 Apr 2026 07:13:37 +0000 Subject: [PATCH] appview/pages: add vouch hats Signed-off-by: oppiliappan --- input.css | 9 +++++++++ appview/issues/issues.go | 60 +++++++++++++++++++++++++++++++++++++++++++----------------- appview/models/issue.go | 12 ++++++------ appview/models/pull.go | 12 ++++++------ appview/models/vouch.go | 43 +++++++++++++++++++++++++++++++++++++++++++ appview/pages/funcmap.go | 2 ++ appview/pages/pages.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ appview/pulls/pulls.go | 45 ++++++++++++++++++++++++++++++++++----------- appview/state/profile.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- appview/notify/db/db.go | 4 ++-- appview/pages/templates/fragments/icons/shield-direct-denounce.html | 5 +++++ appview/pages/templates/fragments/icons/shield-direct-vouch.html | 5 +++++ appview/pages/templates/fragments/icons/shield-empty.html | 5 +++++ appview/pages/templates/fragments/icons/shield-indirect-denounce.html | 7 +++++++ appview/pages/templates/fragments/icons/shield-indirect-vouch.html | 6 ++++++ appview/pages/templates/fragments/icons/shield-mixed.html | 12 ++++++++++++ appview/pages/templates/fragments/icons/shield-plain.html | 5 +++++ appview/pages/templates/repo/issues/issue.html | 7 +++++-- appview/pages/templates/repo/issues/issues.html | 8 +++++++- appview/pages/templates/repo/pulls/pull.html | 18 ++++++++++-------- appview/pages/templates/repo/pulls/pulls.html | 5 +++-- appview/pages/templates/user/fragments/networkVouches.html | 42 ++++++++++++++++++++++++++++++++++++++++++ appview/pages/templates/user/fragments/picLink.html | 52 ++++++++++++++++++++++++++++++++++++++++++---------- appview/pages/templates/user/fragments/vouchButton.html | 30 ++++++++++++++++++++++++++++++ appview/pages/templates/user/fragments/vouchPopover.html | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ appview/pages/templates/repo/issues/fragments/commentList.html | 13 ++++++++----- appview/pages/templates/repo/issues/fragments/issueListing.html | 5 +++-- appview/pages/templates/repo/pulls/fragments/pullHeader.html | 3 ++- 28 file(s) changed, 618 insertion(s)(+), 108 deletion(s)(-) diff --git a/input.css b/input.css --- a/input.css +++ b/input.css @@ -147,6 +147,15 @@ disabled:before:bg-green-400 dark:disabled:before:bg-green-600; } + .btn-cancel { + @apply btn text-white + before:bg-red-600 hover:before:bg-red-700 + dark:before:bg-red-700 dark:hover:before:bg-red-800 + before:border before:border-red-700 hover:before:border-red-800 + focus-visible:before:outline-red-500 + disabled:before:bg-red-400 dark:disabled:before:bg-red-600; + } + .prose { overflow-wrap: anywhere; } diff --git a/appview/issues/issues.go b/appview/issues/issues.go --- a/appview/issues/issues.go +++ b/appview/issues/issues.go @@ -127,21 +127,34 @@ return } + vouchRelationships := make(map[syntax.DID]*models.VouchRelationship) + if user != nil { + participants := issue.Participants() + vouchRelationships, err = db.GetVouchRelationshipsBatch(rp.db, syntax.DID(user.Did), participants) + if err != nil { + l.Error("failed to fetch vouch relationships", "err", err) + } + } + defs := make(map[string]*models.LabelDefinition) for _, l := range labelDefs { defs[l.AtUri().String()] = &l } - rp.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ - LoggedInUser: user, - RepoInfo: rp.repoResolver.GetRepoInfo(r, user), - Issue: issue, - CommentList: issue.CommentList(), - Backlinks: backlinks, - Reactions: reactionMap, - UserReacted: userReactions, - LabelDefs: defs, + err = rp.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ + LoggedInUser: user, + RepoInfo: rp.repoResolver.GetRepoInfo(r, user), + Issue: issue, + CommentList: issue.CommentList(), + Backlinks: backlinks, + Reactions: reactionMap, + UserReacted: userReactions, + LabelDefs: defs, + VouchRelationships: vouchRelationships, }) + if err != nil { + l.Error("failed to render issue", "err", err) + } } func (rp *Issues) EditIssue(w http.ResponseWriter, r *http.Request) { @@ -978,15 +991,28 @@ } } + vouchRelationships := make(map[syntax.DID]*models.VouchRelationship) + if user != nil { + dids := make([]syntax.DID, len(issues)) + for i, u := range issues { + dids[i] = syntax.DID(u.Did) + } + vouchRelationships, err = db.GetVouchRelationshipsBatch(rp.db, syntax.DID(user.Did), dids) + if err != nil { + l.Error("failed to fetch vouch relationships", "err", err) + } + } + rp.pages.RepoIssues(w, pages.RepoIssuesParams{ - LoggedInUser: rp.oauth.GetMultiAccountUser(r), - RepoInfo: repoInfo, - Issues: issues, - IssueCount: totalIssues, - LabelDefs: defs, - FilterState: filterState, - FilterQuery: query.String(), - Page: page, + LoggedInUser: rp.oauth.GetMultiAccountUser(r), + RepoInfo: repoInfo, + Issues: issues, + IssueCount: totalIssues, + LabelDefs: defs, + FilterState: filterState, + FilterQuery: query.String(), + Page: page, + VouchRelationships: vouchRelationships, }) } diff --git a/appview/models/issue.go b/appview/models/issue.go --- a/appview/models/issue.go +++ b/appview/models/issue.go @@ -135,21 +135,21 @@ return listing } -func (i *Issue) Participants() []string { - participantSet := make(map[string]struct{}) - participants := []string{} +func (i *Issue) Participants() []syntax.DID { + participantSet := make(map[syntax.DID]struct{}) + participants := []syntax.DID{} - addParticipant := func(did string) { + addParticipant := func(did syntax.DID) { if _, exists := participantSet[did]; !exists { participantSet[did] = struct{}{} participants = append(participants, did) } } - addParticipant(i.Did) + addParticipant(syntax.DID(i.Did)) for _, c := range i.Comments { - addParticipant(c.Did) + addParticipant(syntax.DID(c.Did)) } return participants diff --git a/appview/models/pull.go b/appview/models/pull.go --- a/appview/models/pull.go +++ b/appview/models/pull.go @@ -386,22 +386,22 @@ return false } -func (p *Pull) Participants() []string { - participantSet := make(map[string]struct{}) - participants := []string{} +func (p *Pull) Participants() []syntax.DID { + participantSet := make(map[syntax.DID]struct{}) + participants := []syntax.DID{} - addParticipant := func(did string) { + addParticipant := func(did syntax.DID) { if _, exists := participantSet[did]; !exists { participantSet[did] = struct{}{} participants = append(participants, did) } } - addParticipant(p.OwnerDid) + addParticipant(syntax.DID(p.OwnerDid)) for _, s := range p.Submissions { for _, sp := range s.Participants() { - addParticipant(sp) + addParticipant(syntax.DID(sp)) } } diff --git a/appview/models/vouch.go b/appview/models/vouch.go --- a/appview/models/vouch.go +++ b/appview/models/vouch.go @@ -102,6 +102,49 @@ return nil } +func (vr *VouchRelationship) IsIndirectVouch() bool { + if vr.IsDirectVouch() || vr.IsDirectDenounce() { + return false + } + for _, v := range vr.NetworkVouches { + if v.Did != vr.ViewerDid && v.Kind == VouchKindVouch { + return true + } + } + return false +} + +func (vr *VouchRelationship) IsIndirectDenounce() bool { + if vr.IsDirectVouch() || vr.IsDirectDenounce() { + return false + } + for _, v := range vr.NetworkVouches { + if v.Did != vr.ViewerDid && v.Kind == VouchKindDenounce { + return true + } + } + return false +} + +func (vr *VouchRelationship) IsMixed() bool { + if vr.IsDirectVouch() || vr.IsDirectDenounce() { + return false + } + hasVouch := false + hasDenounce := false + for _, v := range vr.NetworkVouches { + if v.Did != vr.ViewerDid { + switch v.Kind { + case VouchKindVouch: + hasVouch = true + case VouchKindDenounce: + hasDenounce = true + } + } + } + return hasVouch && hasDenounce +} + func (vr *VouchRelationship) VouchStrength() int { count := 0 for _, v := range vr.NetworkVouches { diff --git a/appview/pages/funcmap.go b/appview/pages/funcmap.go --- a/appview/pages/funcmap.go +++ b/appview/pages/funcmap.go @@ -24,6 +24,7 @@ "github.com/alecthomas/chroma/v2/lexers" "github.com/alecthomas/chroma/v2/styles" "github.com/dustin/go-humanize" + "github.com/dustin/go-humanize/english" "github.com/go-enry/go-enry/v2" "github.com/yuin/goldmark" emoji "github.com/yuin/goldmark-emoji" @@ -177,6 +178,7 @@ return s }, "commaFmt": humanize.Comma, + "plural": english.Plural, "relTimeFmt": humanize.Time, "shortRelTimeFmt": func(t time.Time) string { return humanize.CustomRelTime(t, time.Now(), "", "", []humanize.RelTimeMagnitude{ diff --git a/appview/pages/pages.go b/appview/pages/pages.go --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -624,13 +624,14 @@ } type ProfileCard struct { - UserDid string - HasProfile bool - FollowStatus models.FollowStatus - Punchcard *models.Punchcard - Profile *models.Profile - Stats ProfileStats - Active string + UserDid string + HasProfile bool + FollowStatus models.FollowStatus + VouchRelationship *models.VouchRelationship + Punchcard *models.Punchcard + Profile *models.Profile + Stats ProfileStats + Active string } type ProfileStats struct { @@ -647,6 +648,7 @@ {"repos", "repos", "book-marked", p.Stats.RepoCount}, {"starred", "starred", "star", p.Stats.StarredCount}, {"strings", "strings", "line-squiggle", p.Stats.StringCount}, + {"vouches", "vouches", "shield", nil}, } return tabs @@ -706,6 +708,20 @@ func (p *Pages) ProfileStrings(w io.Writer, params ProfileStringsParams) error { params.Active = "strings" return p.executeProfile("user/strings", w, params) +} + +type ProfileVouchesParams struct { + LoggedInUser *oauth.MultiAccountUser + Vouches []models.Vouch + Suggestions []models.VouchSuggestion + Card *ProfileCard + Page pagination.Page + Active string +} + +func (p *Pages) ProfileVouches(w io.Writer, params ProfileVouchesParams) error { + params.Active = "vouches" + return p.executeProfile("user/vouches", w, params) } type FollowCard struct { @@ -1125,15 +1141,16 @@ } type RepoIssuesParams struct { - LoggedInUser *oauth.MultiAccountUser - RepoInfo repoinfo.RepoInfo - Active string - Issues []models.Issue - IssueCount int - LabelDefs map[string]*models.LabelDefinition - Page pagination.Page - FilterState string - FilterQuery string + LoggedInUser *oauth.MultiAccountUser + RepoInfo repoinfo.RepoInfo + Active string + Issues []models.Issue + IssueCount int + LabelDefs map[string]*models.LabelDefinition + Page pagination.Page + FilterState string + FilterQuery string + VouchRelationships map[syntax.DID]*models.VouchRelationship } func (p *Pages) RepoIssues(w io.Writer, params RepoIssuesParams) error { @@ -1150,8 +1167,9 @@ Backlinks []models.RichReferenceLink LabelDefs map[string]*models.LabelDefinition - Reactions map[models.ReactionKind]models.ReactionDisplayData - UserReacted map[models.ReactionKind]bool + Reactions map[models.ReactionKind]models.ReactionDisplayData + UserReacted map[models.ReactionKind]bool + VouchRelationships map[syntax.DID]*models.VouchRelationship } func (p *Pages) RepoSingleIssue(w io.Writer, params RepoSingleIssueParams) error { @@ -1259,17 +1277,18 @@ } type RepoPullsParams struct { - LoggedInUser *oauth.MultiAccountUser - RepoInfo repoinfo.RepoInfo - Pulls []*models.Pull - Active string - FilterState string - FilterQuery string - Stacks []models.Stack - Pipelines map[string]models.Pipeline - LabelDefs map[string]*models.LabelDefinition - Page pagination.Page - PullCount int + LoggedInUser *oauth.MultiAccountUser + RepoInfo repoinfo.RepoInfo + Pulls []*models.Pull + Active string + FilterState string + FilterQuery string + Stacks []models.Stack + Pipelines map[string]models.Pipeline + LabelDefs map[string]*models.LabelDefinition + Page pagination.Page + PullCount int + VouchRelationships map[syntax.DID]*models.VouchRelationship } func (p *Pages) RepoPulls(w io.Writer, params RepoPullsParams) error { @@ -1314,7 +1333,8 @@ Reactions map[models.ReactionKind]models.ReactionDisplayData UserReacted map[models.ReactionKind]bool - LabelDefs map[string]*models.LabelDefinition + LabelDefs map[string]*models.LabelDefinition + VouchRelationships map[syntax.DID]*models.VouchRelationship } func (p *Pages) RepoSinglePull(w io.Writer, params RepoSinglePullParams) error { diff --git a/appview/pulls/pulls.go b/appview/pulls/pulls.go --- a/appview/pulls/pulls.go +++ b/appview/pulls/pulls.go @@ -266,6 +266,15 @@ defs[l.AtUri().String()] = &l } + vouchRelationships := make(map[syntax.DID]*models.VouchRelationship) + if user != nil { + participants := pull.Participants() + vouchRelationships, err = db.GetVouchRelationshipsBatch(s.db, syntax.DID(user.Did), participants) + if err != nil { + l.Error("failed to fetch vouch relationships", "err", err) + } + } + patch := pull.Submissions[roundIdInt].CombinedPatch() var diff types.DiffRenderer diff = patchutil.AsNiceDiff(patch, pull.TargetBranch) @@ -306,7 +315,8 @@ Reactions: reactionMap, UserReacted: userReactions, - LabelDefs: defs, + LabelDefs: defs, + VouchRelationships: vouchRelationships, }) } @@ -782,17 +792,30 @@ filterState = state.String() } + vouchRelationships := make(map[syntax.DID]*models.VouchRelationship) + if user != nil { + dids := make([]syntax.DID, len(pulls)) + for i, p := range pulls { + dids[i] = syntax.DID(p.OwnerDid) + } + vouchRelationships, err = db.GetVouchRelationshipsBatch(s.db, syntax.DID(user.Did), dids) + if err != nil { + l.Error("failed to fetch vouch relationships", "err", err) + } + } + s.pages.RepoPulls(w, pages.RepoPullsParams{ - LoggedInUser: s.oauth.GetMultiAccountUser(r), - RepoInfo: repoInfo, - Pulls: pulls, - LabelDefs: defs, - FilterState: filterState, - FilterQuery: query.String(), - Stacks: stacks, - Pipelines: m, - Page: page, - PullCount: totalPulls, + LoggedInUser: s.oauth.GetMultiAccountUser(r), + RepoInfo: repoInfo, + Pulls: pulls, + LabelDefs: defs, + FilterState: filterState, + FilterQuery: query.String(), + Stacks: stacks, + Pipelines: m, + Page: page, + PullCount: totalPulls, + VouchRelationships: vouchRelationships, }) } diff --git a/appview/state/profile.go b/appview/state/profile.go --- a/appview/state/profile.go +++ b/appview/state/profile.go @@ -43,6 +43,10 @@ ServeHTTP(w, r) case "strings": s.stringsPage(w, r) + case "vouches": + middleware. + Paginate(http.HandlerFunc(s.vouchesPage)). + ServeHTTP(w, r) default: s.profileOverview(w, r) } @@ -93,9 +97,12 @@ loggedInUser := s.oauth.GetMultiAccountUser(r) followStatus := models.IsNotFollowing var loggedInDid string + var vouchRelationship *models.VouchRelationship + if loggedInUser != nil { followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, did) loggedInDid = loggedInUser.Did + vouchRelationship, err = db.GetVouchRelationship(s.db, syntax.DID(loggedInUser.Did), syntax.DID(did)) } showPunchcard := s.shouldShowPunchcard(did, loggedInDid) @@ -116,10 +123,11 @@ } return &pages.ProfileCard{ - UserDid: did, - HasProfile: hasProfile, - Profile: profile, - FollowStatus: followStatus, + UserDid: did, + HasProfile: hasProfile, + Profile: profile, + FollowStatus: followStatus, + VouchRelationship: vouchRelationship, Stats: pages.ProfileStats{ RepoCount: repoCount, StringCount: stringCount, @@ -177,13 +185,16 @@ l.Error("failed to create timeline", "err", err) } - s.pages.ProfileOverview(w, pages.ProfileOverviewParams{ + err = s.pages.ProfileOverview(w, pages.ProfileOverviewParams{ LoggedInUser: s.oauth.GetMultiAccountUser(r), Card: profile, Repos: pinnedRepos, CollaboratingRepos: pinnedCollaboratingRepos, ProfileTimeline: timeline, }) + if err != nil { + l.Error("failed to render template", "err", err) + } } func (s *State) shouldShowPunchcard(targetDid, requesterDid string) bool { @@ -378,6 +389,65 @@ Strings: strings, Card: profile, }) +} + +func (s *State) vouchesPage(w http.ResponseWriter, r *http.Request) { + l := s.logger.With("handler", "vouchesPage") + + profile, err := s.profile(r) + if err != nil { + l.Error("failed to build profile card", "err", err) + s.pages.Error500(w) + return + } + l = l.With("profileDid", profile.UserDid) + + loggedInUser := s.oauth.GetMultiAccountUser(r) + page := pagination.FromContext(r.Context()) + + var vouches []models.Vouch + if loggedInUser != nil { + vouches, err = db.GetNetworkVouchTimeline(s.db, loggedInUser.Did, profile.UserDid, page) + if err != nil { + l.Error("failed to get vouch timeline", "err", err) + s.pages.Error500(w) + return + } + } + + var suggestions []models.VouchSuggestion + if loggedInUser != nil && loggedInUser.Did == profile.UserDid { + suggestions, err = db.GetVouchSuggestions(s.db, profile.UserDid, 5) + if err != nil { + l.Error("failed to get vouch suggestions", "err", err) + } + + if len(suggestions) > 0 { + suggestionDids := make([]syntax.DID, len(suggestions)) + for i, s := range suggestions { + suggestionDids[i] = syntax.DID(s.Did) + } + relationships, err := db.GetVouchRelationshipsBatch(s.db, syntax.DID(loggedInUser.Did), suggestionDids) + if err != nil { + l.Error("failed to get vouch relationships for suggestions", "err", err) + } else { + for i := range suggestions { + suggestions[i].VouchRelationship = relationships[suggestions[i].Did] + } + } + } + } + + err = s.pages.ProfileVouches(w, pages.ProfileVouchesParams{ + LoggedInUser: loggedInUser, + Vouches: vouches, + Suggestions: suggestions, + Card: profile, + Page: page, + }) + if err != nil { + l.Error("failed to render page", "err", err) + } } type FollowsPageParams struct { diff --git a/appview/notify/db/db.go b/appview/notify/db/db.go --- a/appview/notify/db/db.go +++ b/appview/notify/db/db.go @@ -305,7 +305,7 @@ // - remove those already mentioned recipients := sets.Singleton(syntax.DID(repo.Did)) for _, p := range pull.Participants() { - recipients.Insert(syntax.DID(p)) + recipients.Insert(p) } for _, m := range mentions { recipients.Remove(m) @@ -439,7 +439,7 @@ recipients.Insert(c.SubjectDid) } for _, p := range pull.Participants() { - recipients.Insert(syntax.DID(p)) + recipients.Insert(p) } entityType := "pull" diff --git a/appview/pages/templates/fragments/icons/shield-direct-denounce.html b/appview/pages/templates/fragments/icons/shield-direct-denounce.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/fragments/icons/shield-direct-denounce.html @@ -0,0 +1,5 @@ +{{ define "fragments/icons/shield-direct-denounce" }} + + + +{{ end }} diff --git a/appview/pages/templates/fragments/icons/shield-direct-vouch.html b/appview/pages/templates/fragments/icons/shield-direct-vouch.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/fragments/icons/shield-direct-vouch.html @@ -0,0 +1,5 @@ +{{ define "fragments/icons/shield-direct-vouch" }} + + + +{{ end }} diff --git a/appview/pages/templates/fragments/icons/shield-empty.html b/appview/pages/templates/fragments/icons/shield-empty.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/fragments/icons/shield-empty.html @@ -0,0 +1,5 @@ +{{ define "fragments/icons/shield-empty" }} + + + +{{ end }} diff --git a/appview/pages/templates/fragments/icons/shield-indirect-denounce.html b/appview/pages/templates/fragments/icons/shield-indirect-denounce.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/fragments/icons/shield-indirect-denounce.html @@ -0,0 +1,7 @@ +{{ define "fragments/icons/shield-indirect-denounce" }} + + + + + +{{ end }} diff --git a/appview/pages/templates/fragments/icons/shield-indirect-vouch.html b/appview/pages/templates/fragments/icons/shield-indirect-vouch.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/fragments/icons/shield-indirect-vouch.html @@ -0,0 +1,6 @@ +{{ define "fragments/icons/shield-indirect-vouch" }} + + + + +{{ end }} diff --git a/appview/pages/templates/fragments/icons/shield-mixed.html b/appview/pages/templates/fragments/icons/shield-mixed.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/fragments/icons/shield-mixed.html @@ -0,0 +1,12 @@ +{{ define "fragments/icons/shield-mixed" }} + + + + + + + + + + +{{ end }} diff --git a/appview/pages/templates/fragments/icons/shield-plain.html b/appview/pages/templates/fragments/icons/shield-plain.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/fragments/icons/shield-plain.html @@ -0,0 +1,5 @@ +{{ define "fragments/icons/shield-plain" }} + + + +{{ end }} diff --git a/appview/pages/templates/repo/issues/issue.html b/appview/pages/templates/repo/issues/issue.html --- a/appview/pages/templates/repo/issues/issue.html +++ b/appview/pages/templates/repo/issues/issue.html @@ -68,7 +68,8 @@ opened by - {{ template "user/fragments/picHandleLink" .Issue.Did }} + {{ template "user/fragments/picLink" (list .Issue.Did "size-6" (index .VouchRelationships .Issue.Did)) }} + {{ resolve .Issue.Did }} {{ if .Issue.Edited }} edited {{ template "repo/fragments/time" .Issue.Edited }} @@ -118,7 +119,9 @@ "RepoInfo" $.RepoInfo "LoggedInUser" $.LoggedInUser "Issue" $.Issue - "CommentList" $.Issue.CommentList) + "CommentList" $.Issue.CommentList + "VouchRelationships" $.VouchRelationships + ) }} {{ template "repo/issues/fragments/newComment" . }} diff --git a/appview/pages/templates/repo/issues/issues.html b/appview/pages/templates/repo/issues/issues.html --- a/appview/pages/templates/repo/issues/issues.html +++ b/appview/pages/templates/repo/issues/issues.html @@ -65,7 +65,13 @@ {{ define "repoAfter" }}
- {{ template "repo/issues/fragments/issueListing" (dict "Issues" .Issues "RepoPrefix" .RepoInfo.FullName "LabelDefs" .LabelDefs) }} + {{ template "repo/issues/fragments/issueListing" + (dict + "Issues" .Issues + "RepoPrefix" .RepoInfo.FullName + "LabelDefs" .LabelDefs + "VouchRelationships" .VouchRelationships + ) }}
{{if gt .IssueCount .Page.Limit }} {{ template "fragments/pagination" (dict diff --git a/appview/pages/templates/repo/pulls/pull.html b/appview/pages/templates/repo/pulls/pull.html --- a/appview/pages/templates/repo/pulls/pull.html +++ b/appview/pages/templates/repo/pulls/pull.html @@ -300,7 +300,7 @@ flex gap-2 sticky top-0 z-20">
- {{ template "user/fragments/picLink" (list $root.Pull.OwnerDid "size-8") }} + {{ template "user/fragments/picLink" (list $root.Pull.OwnerDid "size-8" (index $root.VouchRelationships $root.Pull.OwnerDid)) }}
@@ -592,7 +592,7 @@
{{ range $item.Comments }} - {{ template "submissionComment" . }} + {{ template "submissionComment" (list . $root) }} {{ end }}
{{ if gt $c 0}} @@ -625,25 +625,27 @@ {{ end }} {{ define "submissionComment" }} -
+ {{ $comment := index . 0 }} + {{ $root := index . 1 }} +
- {{ template "user/fragments/picLink" (list .OwnerDid "size-8") }} + {{ template "user/fragments/picLink" (list $comment.OwnerDid "size-8" (index $root.VouchRelationships $comment.OwnerDid)) }}
- {{ .Body | markdown }} + {{ $comment.Body | markdown }}
diff --git a/appview/pages/templates/repo/pulls/pulls.html b/appview/pages/templates/repo/pulls/pulls.html --- a/appview/pages/templates/repo/pulls/pulls.html +++ b/appview/pages/templates/repo/pulls/pulls.html @@ -81,8 +81,9 @@
{{ template "repo/pulls/fragments/pullState" $topPR.State }} - - {{ template "user/fragments/picHandleLink" $topPR.OwnerDid }} + + {{ template "user/fragments/picLink" (list $topPR.OwnerDid "size-6" (index $.VouchRelationships $topPR.OwnerDid)) }} + {{ resolve $topPR.OwnerDid }} diff --git a/appview/pages/templates/user/fragments/networkVouches.html b/appview/pages/templates/user/fragments/networkVouches.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/user/fragments/networkVouches.html @@ -0,0 +1,42 @@ +{{/* Takes a VouchRelationship directly */}} +{{ define "user/fragments/networkVouches" }} + {{ $vouches := (list) }} + {{ $denounces := (list) }} + {{ range .IndirectVouches }} + {{ if .IsVouch }} + {{ $vouches = append $vouches .Did }} + {{ else if .IsDenounce }} + {{ $denounces = append $denounces .Did }} + {{ end }} + {{ end }} + +
+ {{ if $vouches }} +
+ + vouched for by {{ len $vouches }} {{ if eq (len $vouches) 1 }}user{{ else }}users{{ end }} + + {{ template "fragments/tinyAvatarList" (dict "all" $vouches "classes" "size-6") }} +
+ {{ end }} + + {{ if $denounces }} +
+ + denounced by {{ len $denounces }} {{ if eq (len $denounces) 1 }}user{{ else }}users{{ end }} + + {{ template "fragments/tinyAvatarList" (dict "all" $denounces "classes" "size-6") }} +
+ {{ end }} +
+{{ end }} diff --git a/appview/pages/templates/user/fragments/picLink.html b/appview/pages/templates/user/fragments/picLink.html --- a/appview/pages/templates/user/fragments/picLink.html +++ b/appview/pages/templates/user/fragments/picLink.html @@ -2,14 +2,46 @@ {{ $did := index . 0 }} {{ $classes := index . 1 }} {{ $handle := resolve $did }} - - - + {{ $vr := false }} + {{ if ge (len .) 3 }} + {{ $vr = index . 2 }} + {{ end }} +
+ + + + {{ if $vr }} + {{ if ne $did $vr.ViewerDid }} + + {{ end }} + {{ end }} +
+ {{ if $vr }} + {{ if ne $did $vr.ViewerDid }} + {{ template "user/fragments/vouchPopover" (dict "VouchRelationship" $vr) }} + {{ end }} + {{ end }} {{ end }} - - - diff --git a/appview/pages/templates/user/fragments/vouchButton.html b/appview/pages/templates/user/fragments/vouchButton.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/user/fragments/vouchButton.html @@ -0,0 +1,30 @@ +{{ define "user/fragments/vouchButton" }} +{{ $isVouched := false }} +{{ $isDenounced := false }} +{{ if .VouchRelationship }} + {{ if .VouchRelationship.IsDirectVouch }} + {{ $isVouched = true }} + {{ else if .VouchRelationship.IsDirectDenounce }} + {{ $isDenounced = true }} + {{ end }} +{{ end }} + +{{ $userDid := "" }} +{{ with .VouchRelationship }} + {{ $userDid = .SubjectDid }} +{{ end }} + +{{ end }} diff --git a/appview/pages/templates/user/fragments/vouchPopover.html b/appview/pages/templates/user/fragments/vouchPopover.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/user/fragments/vouchPopover.html @@ -0,0 +1,151 @@ +{{ define "user/fragments/vouchPopover" }} +{{ $isVouched := false }} +{{ $isDenounced := false }} +{{ $isUndecided := false }} +{{ if .VouchRelationship }} + {{ if .VouchRelationship.IsDirectVouch }} + {{ $isVouched = true }} + {{ else if .VouchRelationship.IsDirectDenounce }} + {{ $isDenounced = true }} + {{ else }} + {{ $isUndecided = true }} + {{ end }} +{{ else }} + {{ $isUndecided = true }} +{{ end }} + +{{ $userIdent := "" }} +{{ $userDid := "" }} +{{ with .VouchRelationship }} + {{ $userDid = .SubjectDid }} + {{ $userIdent = resolve .SubjectDid }} +{{ end }} +
+ +
+ +
+

Vouch for {{ $userIdent }}

+

+ {{ with .VouchRelationship }} + {{ with .GetDirectVouch }} + You {{if $isVouched}}vouched{{else}}denounced{{end}} {{ $userIdent }} {{ relTimeFmt .CreatedAt }}. + You can change your descision below. + {{ else }} + Vouching builds a web-of-trust across Tangled. Vouch for users you + have had positive interactions with. + {{ end }} + {{ end }} +

+
+ + {{ if .VouchRelationship }} + {{ if .VouchRelationship.IndirectVouches }} +
+

From your network:

+ {{ template "user/fragments/networkVouches" .VouchRelationship }} +
+ {{ end }} + {{ end }} + + + + {{ $labelClass := "grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded p-2 py- ring-1 ring-gray-200 dark:ring-gray-700 cursor-pointer" }} + +
+ + + + + + + + + +
+ +
+ + +
+
+ +
+
+{{ end }} diff --git a/appview/pages/templates/repo/issues/fragments/commentList.html b/appview/pages/templates/repo/issues/fragments/commentList.html --- a/appview/pages/templates/repo/issues/fragments/commentList.html +++ b/appview/pages/templates/repo/issues/fragments/commentList.html @@ -14,7 +14,9 @@ "RepoInfo" $root.RepoInfo "LoggedInUser" $root.LoggedInUser "Issue" $root.Issue - "Comment" $comment.Self) }} + "Comment" $comment.Self + "VouchRelationship" (index $root.VouchRelationships $comment.Self.Did) + ) }}
{{ template "topLevelComment" $params }} @@ -28,8 +30,9 @@ "RepoInfo" $root.RepoInfo "LoggedInUser" $root.LoggedInUser "Issue" $root.Issue - "Comment" $reply) - }} + "Comment" $reply + "VouchRelationship" (index $root.VouchRelationships $reply.Did) + ) }}
{{ end }}
@@ -41,7 +44,7 @@ {{ define "topLevelComment" }}
- {{ template "user/fragments/picLink" (list .Comment.Did "size-8 mr-1") }} + {{ template "user/fragments/picLink" (list .Comment.Did "size-8 mr-1" .VouchRelationship) }}
{{ template "repo/issues/fragments/issueCommentHeader" . }} @@ -53,7 +56,7 @@ {{ define "replyComment" }}
- {{ template "user/fragments/picLink" (list .Comment.Did "size-8 mr-1") }} + {{ template "user/fragments/picLink" (list .Comment.Did "size-8 mr-1" .VouchRelationship) }}
{{ template "repo/issues/fragments/issueCommentHeader" . }} diff --git a/appview/pages/templates/repo/issues/fragments/issueListing.html b/appview/pages/templates/repo/issues/fragments/issueListing.html --- a/appview/pages/templates/repo/issues/fragments/issueListing.html +++ b/appview/pages/templates/repo/issues/fragments/issueListing.html @@ -26,8 +26,9 @@ {{ $state }} - - {{ template "user/fragments/picHandleLink" .Did }} + + {{ template "user/fragments/picLink" (list .Did "size-6" (index $.VouchRelationships .Did)) }} + {{ resolve .Did }} diff --git a/appview/pages/templates/repo/pulls/fragments/pullHeader.html b/appview/pages/templates/repo/pulls/fragments/pullHeader.html --- a/appview/pages/templates/repo/pulls/fragments/pullHeader.html +++ b/appview/pages/templates/repo/pulls/fragments/pullHeader.html @@ -11,7 +11,8 @@ {{ template "repo/pulls/fragments/pullState" .Pull.State }} opened by - {{ template "user/fragments/picHandleLink" .Pull.OwnerDid }} + {{ template "user/fragments/picLink" (list .Pull.OwnerDid "size-6" (index .VouchRelationships .Pull.OwnerDid)) }} + {{ resolve .Pull.OwnerDid }} {{ template "repo/fragments/time" .Pull.Created }} -- tangled.sh