diff --git a/ogre/client.go b/ogre/client.go
--- a/ogre/client.go
+++ b/ogre/client.go
@@ -47,24 +47,28 @@
}
type IssueCardPayload struct {
- Type string `json:"type"`
- RepoName string `json:"repoName"`
- OwnerHandle string `json:"ownerHandle"`
- AvatarUrl string `json:"avatarUrl"`
- Title string `json:"title"`
- IssueNumber int `json:"issueNumber"`
- Status string `json:"status"`
- Labels []LabelData `json:"labels"`
- CommentCount int `json:"commentCount"`
- ReactionCount int `json:"reactionCount"`
- CreatedAt string `json:"createdAt"`
+ Type string `json:"type"`
+ RepoName string `json:"repoName"`
+ OwnerHandle string `json:"ownerHandle"`
+ AuthorHandle string `json:"authorHandle"`
+ AvatarUrl string `json:"avatarUrl"`
+ AuthorAvatarUrl string `json:"authorAvatarUrl"`
+ Title string `json:"title"`
+ IssueNumber int `json:"issueNumber"`
+ Status string `json:"status"`
+ Labels []LabelData `json:"labels"`
+ CommentCount int `json:"commentCount"`
+ ReactionCount int `json:"reactionCount"`
+ CreatedAt string `json:"createdAt"`
}
type PullRequestCardPayload struct {
Type string `json:"type"`
RepoName string `json:"repoName"`
OwnerHandle string `json:"ownerHandle"`
+ AuthorHandle string `json:"authorHandle"`
AvatarUrl string `json:"avatarUrl"`
+ AuthorAvatarUrl string `json:"authorAvatarUrl"`
Title string `json:"title"`
PullRequestNumber int `json:"pullRequestNumber"`
Status string `json:"status"`
diff --git a/appview/issues/opengraph.go b/appview/issues/opengraph.go
--- a/appview/issues/opengraph.go
+++ b/appview/issues/opengraph.go
@@ -68,8 +68,10 @@
}
ownerHandle := rp.pages.DisplayHandle(r.Context(), f.Did)
+ authorHandle := rp.pages.DisplayHandle(r.Context(), issue.Did)
avatarUrl := rp.pages.AvatarUrl(f.Did, "256")
+ authorAvatarUrl := rp.pages.AvatarUrl(issue.Did, "256")
status := "closed"
if issue.Open {
@@ -81,17 +83,19 @@
reactionCount, _ := db.GetReactionCount(rp.db, issue.AtUri())
payload := ogre.IssueCardPayload{
- Type: "issue",
- RepoName: f.Name,
- OwnerHandle: ownerHandle,
- AvatarUrl: avatarUrl,
- Title: issue.Title,
- IssueNumber: issue.IssueId,
- Status: status,
- Labels: labels,
- CommentCount: commentCount,
- ReactionCount: reactionCount,
- CreatedAt: issue.Created.Format(time.RFC3339),
+ Type: "issue",
+ RepoName: f.Name,
+ OwnerHandle: ownerHandle,
+ AuthorHandle: authorHandle,
+ AvatarUrl: avatarUrl,
+ AuthorAvatarUrl: authorAvatarUrl,
+ Title: issue.Title,
+ IssueNumber: issue.IssueId,
+ Status: status,
+ Labels: labels,
+ CommentCount: commentCount,
+ ReactionCount: reactionCount,
+ CreatedAt: issue.Created.Format(time.RFC3339),
}
imageBytes, err := rp.ogreClient.RenderIssueCard(r.Context(), payload)
diff --git a/appview/pulls/opengraph.go b/appview/pulls/opengraph.go
--- a/appview/pulls/opengraph.go
+++ b/appview/pulls/opengraph.go
@@ -26,8 +26,10 @@
}
ownerHandle := s.pages.DisplayHandle(r.Context(), f.Did)
+ authorHandle := s.pages.DisplayHandle(r.Context(), pull.OwnerDid)
avatarUrl := s.pages.AvatarUrl(f.Did, "256")
+ authorAvatarUrl := s.pages.AvatarUrl(pull.OwnerDid, "256")
var status string
if pull.State.IsOpen() {
@@ -60,7 +62,9 @@
Type: "pullRequest",
RepoName: f.Name,
OwnerHandle: ownerHandle,
+ AuthorHandle: authorHandle,
AvatarUrl: avatarUrl,
+ AuthorAvatarUrl: authorAvatarUrl,
Title: pull.Title,
PullRequestNumber: pull.PullId,
Status: status,
diff --git a/ogre/src/validation.ts b/ogre/src/validation.ts
--- a/ogre/src/validation.ts
+++ b/ogre/src/validation.ts
@@ -23,7 +23,9 @@
type: z.literal("issue"),
repoName: z.string().min(1).max(100),
ownerHandle: z.string().min(1).max(100),
+ authorHandle: z.string().min(1).max(100),
avatarUrl: z.string().url(),
+ authorAvatarUrl: z.string().url(),
title: z.string().min(1).max(500),
issueNumber: z.number().int().positive(),
status: z.enum(["open", "closed"]),
@@ -44,7 +46,9 @@
type: z.literal("pullRequest"),
repoName: z.string().min(1).max(100),
ownerHandle: z.string().min(1).max(100),
+ authorHandle: z.string().min(1).max(100),
avatarUrl: z.string().url(),
+ authorAvatarUrl: z.string().url(),
title: z.string().min(1).max(500),
pullRequestNumber: z.number().int().positive(),
status: z.enum(["open", "closed", "merged"]),
diff --git a/ogre/src/__tests__/fixtures.ts b/ogre/src/__tests__/fixtures.ts
--- a/ogre/src/__tests__/fixtures.ts
+++ b/ogre/src/__tests__/fixtures.ts
@@ -32,7 +32,9 @@
type: "issue",
repoName: "core",
ownerHandle: "tangled.org",
+ authorHandle: "oppi.li",
avatarUrl,
+ authorAvatarUrl: avatarUrl,
title: "feature request: sync fork button",
issueNumber: 8,
status: "open",
@@ -54,7 +56,9 @@
type: "pullRequest",
repoName: "core",
ownerHandle: "tangled.org",
+ authorHandle: "oppi.li",
avatarUrl,
+ authorAvatarUrl: avatarUrl,
title: "add author description to README.md",
pullRequestNumber: 1,
status: "open",
diff --git a/ogre/src/__tests__/render.test.ts b/ogre/src/__tests__/render.test.ts
--- a/ogre/src/__tests__/render.test.ts
+++ b/ogre/src/__tests__/render.test.ts
@@ -86,6 +86,17 @@
const validated = issueCardSchema.parse(data);
await renderAndSave(h(IssueCard, validated), "issue-card-long-title.png");
});
+
+ test("renders issue with long author handle (reactions hidden)", async () => {
+ const data = createIssueData(avatarDataUri, {
+ authorHandle: "extremely-long-handle.example.com",
+ });
+ const validated = issueCardSchema.parse(data);
+ await renderAndSave(
+ h(IssueCard, validated),
+ "issue-card-long-handle.png",
+ );
+ });
});
describe("pull request cards", () => {
@@ -132,6 +143,17 @@
await renderAndSave(
h(PullRequestCard, validated),
"pull-request-card-long-title.png",
+ );
+ });
+
+ test("renders pull request with long author handle (reactions hidden)", async () => {
+ const data = createPullRequestData(avatarDataUri, {
+ authorHandle: "extremely-long-handle.example.com",
+ });
+ const validated = pullRequestCardSchema.parse(data);
+ await renderAndSave(
+ h(PullRequestCard, validated),
+ "pull-request-card-long-handle.png",
);
});
});
diff --git a/ogre/src/components/cards/issue.tsx b/ogre/src/components/cards/issue.tsx
--- a/ogre/src/components/cards/issue.tsx
+++ b/ogre/src/components/cards/issue.tsx
@@ -42,6 +42,8 @@
}}>
diff --git a/ogre/src/components/cards/pull-request.tsx b/ogre/src/components/cards/pull-request.tsx
--- a/ogre/src/components/cards/pull-request.tsx
+++ b/ogre/src/components/cards/pull-request.tsx
@@ -127,6 +127,8 @@
}}>
diff --git a/ogre/src/components/shared/footer-stats.tsx b/ogre/src/components/shared/footer-stats.tsx
--- a/ogre/src/components/shared/footer-stats.tsx
+++ b/ogre/src/components/shared/footer-stats.tsx
@@ -1,15 +1,27 @@
import { Row } from "./layout";
import { Calendar, MessageSquare, SmilePlus } from "../../icons/lucide";
import { StatItem } from "./stat-item";
+import { Avatar } from "./avatar";
+import { TYPOGRAPHY } from "./constants";
+
+// Handles longer than this cause the footer to overflow when combined with
+// other stats, so we drop the less-important reaction count first, then the
+// comment count once the handle grows longer still.
+const LONG_HANDLE_THRESHOLD = 20;
+const VERY_LONG_HANDLE_THRESHOLD = 28;
interface FooterStatsProps {
createdAt: string;
+ authorHandle?: string;
+ authorAvatarUrl?: string;
reactionCount?: number;
commentCount?: number;
}
export function FooterStats({
createdAt,
+ authorHandle,
+ authorAvatarUrl,
reactionCount,
commentCount,
}: FooterStatsProps) {
@@ -19,13 +31,38 @@
year: "numeric",
}).format(new Date(createdAt));
+ const handleLength = authorHandle?.length ?? 0;
+ // Long handles crowd the footer. Drop reactions first; drop comments too
+ // for extremely long handles to prevent overflow past the tangled logo.
+ const isLongHandle = handleLength > LONG_HANDLE_THRESHOLD;
+ const isVeryLongHandle = handleLength > VERY_LONG_HANDLE_THRESHOLD;
+ const gap = isLongHandle ? 40 : 64;
+ const hideReactions = isLongHandle;
+ const hideComments = isVeryLongHandle;
+
return (
-
+
+ {authorHandle && authorAvatarUrl ? (
+
+
+
+ {authorHandle}
+
+
+ ) : null}
- {reactionCount ? (
+ {reactionCount && !hideReactions ? (
) : null}
- {commentCount ? (
+ {commentCount && !hideComments ? (
) : null}