Monorepo for Tangled tangled.org
854
fork

Configure Feed

Select the types of activity you want to include in your feed.

ogre/footer-stats: progressively hide footer items on long handles #8

When the author handle is very long, the footer overflows past the Tangled logo on issue/PR OG images. The existing two thresholds (hide reactions at >20 chars, hide comments at >28 chars) were insufficient because even with only avatar + username + date visible, the layout could exceed the available 848px.

Add a third threshold (>40 chars) that also hides the date, and reduce username maxWidth from 480 to 440 for the 29-40 char range.

Signed-off-by: eti eti@eti.tf

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:xu5apv6kmu5jp7g5hwdnej42/sh.tangled.repo.pull/3mkb56vi3ob22
+51 -107
Interdiff #0 #1
-16
ogre/src/__tests__/fixtures.ts
··· 90 90 ...overrides, 91 91 }); 92 92 93 - export const createExtremeHandleIssueData = ( 94 - avatarUrl: string, 95 - overrides?: Partial<IssueCardData>, 96 - ): IssueCardData => ({ 97 - ...createIssueData(avatarUrl), 98 - authorHandle: "some-very-long-username-here.with.a.long.domain", 99 - ...overrides, 100 - }); 101 93 102 - export const createExtremeHandlePullRequestData = ( 103 - avatarUrl: string, 104 - overrides?: Partial<PullRequestCardData>, 105 - ): PullRequestCardData => ({ 106 - ...createPullRequestData(avatarUrl), 107 - authorHandle: "some-very-long-username-here.with.a.long.domain", 108 - ...overrides, 109 - });
+4 -24
ogre/src/__tests__/render.test.ts
··· 17 17 createPullRequestData, 18 18 createLongTitleIssueData, 19 19 createLongTitlePullRequestData, 20 - createExtremeHandleIssueData, 21 - createExtremeHandlePullRequestData, 22 20 } from "./fixtures"; 23 21 24 22 const outputDir = join(process.cwd(), "output"); ··· 89 87 await renderAndSave(h(IssueCard, validated), "issue-card-long-title.png"); 90 88 }); 91 89 92 - test("renders issue with long author handle (reactions hidden)", async () => { 90 + test("renders issue with long author handle", async () => { 93 91 const data = createIssueData(avatarDataUri, { 94 - authorHandle: "extremely-long-handle.example.com", 92 + authorHandle: "very.very.long.tangled.org", 95 93 }); 96 94 const validated = issueCardSchema.parse(data); 97 95 await renderAndSave( ··· 99 97 "issue-card-long-handle.png", 100 98 ); 101 99 }); 102 - 103 - test("renders issue with extreme author handle (date hidden)", async () => { 104 - const data = createExtremeHandleIssueData(avatarDataUri); 105 - const validated = issueCardSchema.parse(data); 106 - await renderAndSave( 107 - h(IssueCard, validated), 108 - "issue-card-extreme-handle.png", 109 - ); 110 - }); 111 100 }); 112 101 113 102 describe("pull request cards", () => { ··· 157 146 ); 158 147 }); 159 148 160 - test("renders pull request with long author handle (reactions hidden)", async () => { 149 + test("renders pull request with long author handle", async () => { 161 150 const data = createPullRequestData(avatarDataUri, { 162 - authorHandle: "extremely-long-handle.example.com", 151 + authorHandle: "very.very.long.tangled.org", 163 152 }); 164 153 const validated = pullRequestCardSchema.parse(data); 165 154 await renderAndSave( ··· 167 156 "pull-request-card-long-handle.png", 168 157 ); 169 158 }); 170 - 171 - test("renders pull request with extreme author handle (date hidden)", async () => { 172 - const data = createExtremeHandlePullRequestData(avatarDataUri); 173 - const validated = pullRequestCardSchema.parse(data); 174 - await renderAndSave( 175 - h(PullRequestCard, validated), 176 - "pull-request-card-extreme-handle.png", 177 - ); 178 - }); 179 159 });
+45 -65
ogre/src/components/shared/footer-stats.tsx
··· 4 4 import { Avatar } from "./avatar"; 5 5 import { TYPOGRAPHY } from "./constants"; 6 6 7 - // Handles longer than this cause the footer to overflow when combined with 8 - // other stats, so we drop the less-important reaction count first, then the 9 - // comment count once the handle grows longer still. 10 - const LONG_HANDLE_THRESHOLD = 20; 11 - const VERY_LONG_HANDLE_THRESHOLD = 28; 12 - const EXTREME_HANDLE_THRESHOLD = 40; 13 - 14 7 interface FooterStatsProps { 15 - createdAt: string; 16 - authorHandle?: string; 17 - authorAvatarUrl?: string; 18 - reactionCount?: number; 19 - commentCount?: number; 8 + createdAt: string; 9 + authorHandle?: string; 10 + authorAvatarUrl?: string; 11 + reactionCount?: number; 12 + commentCount?: number; 20 13 } 21 14 22 15 export function FooterStats({ 23 - createdAt, 24 - authorHandle, 25 - authorAvatarUrl, 26 - reactionCount, 27 - commentCount, 16 + createdAt, 17 + authorHandle, 18 + authorAvatarUrl, 19 + reactionCount, 20 + commentCount, 28 21 }: FooterStatsProps) { 29 - const formattedDate = new Intl.DateTimeFormat("en-GB", { 30 - day: "numeric", 31 - month: "short", 32 - year: "numeric", 33 - }).format(new Date(createdAt)); 34 - 35 - const handleLength = authorHandle?.length ?? 0; 36 - const isLongHandle = handleLength > LONG_HANDLE_THRESHOLD; 37 - const isVeryLongHandle = handleLength > VERY_LONG_HANDLE_THRESHOLD; 38 - const isExtremeHandle = handleLength > EXTREME_HANDLE_THRESHOLD; 39 - 40 - // Progressively drop less important items as the handle gets longer: 41 - // ≤20 chars — show everything, generous gap 42 - // 21-28 chars — hide reactions, tighter gap 43 - // 29-40 chars — also hide comments, reduce username maxWidth 44 - // >40 chars — also hide the date, give username more room 45 - const gap = isLongHandle ? 40 : 64; 46 - const hideReactions = isLongHandle; 47 - const hideComments = isVeryLongHandle; 48 - const hideDate = isExtremeHandle; 49 - const nameMaxWidth = isExtremeHandle ? 700 : isVeryLongHandle ? 440 : 480; 50 - 51 - return ( 52 - <Row style={{ gap }}> 53 - {authorHandle && authorAvatarUrl ? ( 54 - <Row style={{ gap: 16, alignItems: "center" }}> 55 - <Avatar src={authorAvatarUrl} size={40} /> 56 - <span 57 - style={{ 58 - ...TYPOGRAPHY.body, 59 - color: "#404040", 60 - maxWidth: nameMaxWidth, 61 - overflow: "hidden", 62 - textOverflow: "ellipsis", 63 - whiteSpace: "nowrap", 64 - }}> 65 - {authorHandle} 66 - </span> 22 + const formattedDate = new Intl.DateTimeFormat("en-GB", { 23 + day: "numeric", 24 + month: "short", 25 + year: "numeric", 26 + }).format(new Date(createdAt)); 27 + 28 + const handleLength = authorHandle?.length ?? 0; 29 + const showReactions = handleLength <= 16; 30 + const showComments = handleLength <= 11; 31 + 32 + return ( 33 + <Row style={{ gap: 40 }}> 34 + {authorHandle && authorAvatarUrl ? ( 35 + <Row style={{ gap: 16, alignItems: "center" }}> 36 + <Avatar src={authorAvatarUrl} size={40} /> 37 + <span 38 + style={{ 39 + ...TYPOGRAPHY.body, 40 + color: "#404040", 41 + maxWidth: 400, 42 + lineClamp: 1, 43 + display: "block", 44 + }}> 45 + {authorHandle} 46 + </span> 47 + </Row> 48 + ) : null} 49 + <StatItem Icon={Calendar} value={formattedDate} /> 50 + {showReactions && reactionCount ? ( 51 + <StatItem Icon={SmilePlus} value={reactionCount} /> 52 + ) : null} 53 + {showComments && commentCount ? ( 54 + <StatItem Icon={MessageSquare} value={commentCount} /> 55 + ) : null} 67 56 </Row> 68 - ) : null} 69 - {!hideDate && <StatItem Icon={Calendar} value={formattedDate} />} 70 - {reactionCount && !hideReactions ? ( 71 - <StatItem Icon={SmilePlus} value={reactionCount} /> 72 - ) : null} 73 - {commentCount && !hideComments ? ( 74 - <StatItem Icon={MessageSquare} value={commentCount} /> 75 - ) : null} 76 - </Row> 77 - ); 57 + ); 78 58 }
+2 -2
ogre/src/components/cards/issue.tsx
··· 37 37 38 38 <Row 39 39 style={{ 40 - alignItems: "flex-end", 41 - justifyContent: "space-between", 40 + alignItems: "flex-end", 41 + justifyContent: "space-between", 42 42 }}> 43 43 <FooterStats 44 44 createdAt={data.createdAt}

History

2 rounds 0 comments
sign up or login to add to the discussion
eti.tf submitted #1
1 commit
expand
ogre/footer: use char-length thresholds to hide stats on long handles
merge conflicts detected
expand
  • ogre/src/__tests__/fixtures.ts:89
  • ogre/src/__tests__/render.test.ts:87
  • ogre/src/components/cards/issue.tsx:37
  • ogre/src/components/shared/footer-stats.tsx:4
expand 0 comments
eti.tf submitted #0
1 commit
expand
ogre/footer-stats: progressively hide footer items on long handles
expand 0 comments