Detailed changes
@@ -19,7 +19,7 @@ type Documents = {
"\n mutation BugAddCommentAndReopen($input: BugAddCommentAndReopenInput!) {\n bugAddCommentAndReopen(input: $input) {\n bug {\n id\n }\n }\n }\n": typeof types.BugAddCommentAndReopenDocument,
"\n mutation BugStatusOpen($input: BugStatusOpenInput!) {\n bugStatusOpen(input: $input) {\n bug {\n id\n status\n }\n }\n }\n": typeof types.BugStatusOpenDocument,
"\n mutation BugStatusClose($input: BugStatusCloseInput!) {\n bugStatusClose(input: $input) {\n bug {\n id\n status\n }\n }\n }\n": typeof types.BugStatusCloseDocument,
- "\n mutation BugChangeLabels($input: BugChangeLabelInput) {\n bugChangeLabels(input: $input) {\n bug {\n id\n labels {\n name\n color {\n R\n G\n B\n }\n }\n }\n }\n }\n": typeof types.BugChangeLabelsDocument,
+ "\n mutation BugChangeLabels($input: BugChangeLabelInput) {\n bugChangeLabels(input: $input) {\n bug {\n id\n labels {\n name\n ...LabelFields\n }\n }\n }\n }\n": typeof types.BugChangeLabelsDocument,
"\n fragment BugCreateCommentFields on BugCreateTimelineItem {\n author {\n ...IdentitySummary\n }\n message\n createdAt\n lastEdit\n edited\n }\n": typeof types.BugCreateCommentFieldsFragmentDoc,
"\n fragment BugAddCommentFields on BugAddCommentTimelineItem {\n author {\n ...IdentitySummary\n }\n message\n createdAt\n lastEdit\n edited\n }\n": typeof types.BugAddCommentFieldsFragmentDoc,
"\n fragment LabelChangeFields on BugLabelChangeTimelineItem {\n author {\n humanId\n displayName\n }\n date\n added {\n ...LabelFields\n }\n removed {\n ...LabelFields\n }\n }\n": typeof types.LabelChangeFieldsFragmentDoc,
@@ -30,7 +30,7 @@ type Documents = {
"\n query CommitList($repo: String, $ref: String!, $path: String, $after: String, $first: Int) {\n repository(ref: $repo) {\n commits(ref: $ref, path: $path, after: $after, first: $first) {\n nodes {\n hash\n shortHash\n message\n authorName\n date\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }\n": typeof types.CommitListDocument,
"\n query FileDiff($repo: String, $hash: String!, $path: String!) {\n repository(ref: $repo) {\n commit(hash: $hash) {\n diff(path: $path) {\n path\n oldPath\n isBinary\n isNew\n isDelete\n hunks {\n oldStart\n oldLines\n newStart\n newLines\n lines {\n type\n content\n oldLine\n newLine\n }\n }\n }\n }\n }\n }\n": typeof types.FileDiffDocument,
"\n fragment IdentitySummary on Identity {\n id\n humanId\n displayName\n avatarUrl\n }\n": typeof types.IdentitySummaryFragmentDoc,
- "\n fragment BugSummary on Bug {\n id\n humanId\n status\n title\n labels {\n ...LabelFields\n }\n author {\n ...IdentitySummary\n }\n createdAt\n comments {\n totalCount\n }\n }\n": typeof types.BugSummaryFragmentDoc,
+ "\n fragment BugSummary on Bug {\n id\n humanId\n status\n title\n labels {\n name\n ...LabelFields\n }\n author {\n ...IdentitySummary\n }\n createdAt\n comments {\n totalCount\n }\n }\n": typeof types.BugSummaryFragmentDoc,
"\n fragment LabelFields on Label {\n name\n color {\n R\n G\n B\n }\n }\n": typeof types.LabelFieldsFragmentDoc,
"\n query UserIdentity {\n repository {\n userIdentity {\n id\n humanId\n name\n displayName\n avatarUrl\n email\n login\n }\n }\n }\n": typeof types.UserIdentityDocument,
"\n query CodePageRefs($repo: String) {\n repository(ref: $repo) {\n name\n head {\n shortName\n }\n refs {\n nodes {\n name\n shortName\n type\n hash\n }\n }\n }\n }\n": typeof types.CodePageRefsDocument,
@@ -39,7 +39,7 @@ type Documents = {
"\n query CodePageLastCommits(\n $repo: String\n $ref: String!\n $path: String\n $names: [String!]!\n ) {\n repository(ref: $repo) {\n lastCommits(ref: $ref, path: $path, names: $names) {\n name\n commit {\n hash\n shortHash\n message\n date\n }\n }\n }\n }\n": typeof types.CodePageLastCommitsDocument,
"\n query CodePageReadme($repo: String, $ref: String!, $path: String!) {\n repository(ref: $repo) {\n blob(ref: $ref, path: $path) {\n text\n }\n }\n }\n": typeof types.CodePageReadmeDocument,
"\n query AllIdentities($ref: String) {\n repository(ref: $ref) {\n allIdentities(first: 1000) {\n nodes {\n id\n humanId\n name\n email\n login\n displayName\n avatarUrl\n }\n }\n }\n }\n": typeof types.AllIdentitiesDocument,
- "\n query ValidLabels($ref: String) {\n repository(ref: $ref) {\n validLabels {\n nodes {\n name\n color {\n R\n G\n B\n }\n }\n }\n }\n }\n": typeof types.ValidLabelsDocument,
+ "\n query ValidLabels($ref: String) {\n repository(ref: $ref) {\n validLabels {\n nodes {\n name\n color {\n R\n G\n B\n }\n ...LabelFields\n }\n }\n }\n }\n": typeof types.ValidLabelsDocument,
"\n query BugDetail($ref: String, $prefix: String!) {\n repository(ref: $ref) {\n bug(prefix: $prefix) {\n ...BugSummary\n lastEdit\n participants(first: 20) {\n nodes {\n ...IdentitySummary\n }\n }\n timeline(first: 250) {\n nodes {\n __typename\n id\n ... on BugCreateTimelineItem {\n ...BugCreateCommentFields\n }\n ... on BugAddCommentTimelineItem {\n ...BugAddCommentFields\n }\n ... on BugLabelChangeTimelineItem {\n ...LabelChangeFields\n }\n ... on BugSetStatusTimelineItem {\n ...StatusChangeFields\n }\n ... on BugSetTitleTimelineItem {\n ...TitleChangeFields\n }\n }\n }\n }\n }\n }\n": typeof types.BugDetailDocument,
"\n query BugList(\n $ref: String\n $openQuery: String!\n $closedQuery: String!\n $listQuery: String!\n $first: Int\n $after: String\n ) {\n repository(ref: $ref) {\n openCount: allBugs(query: $openQuery, first: 1) {\n totalCount\n }\n closedCount: allBugs(query: $closedQuery, first: 1) {\n totalCount\n }\n bugs: allBugs(query: $listQuery, first: $first, after: $after) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n ...BugSummary\n }\n }\n }\n }\n": typeof types.BugListDocument,
"\n mutation BugCreate($input: BugCreateInput!) {\n bugCreate(input: $input) {\n bug {\n id\n humanId\n }\n }\n }\n": typeof types.BugCreateDocument,
@@ -53,7 +53,7 @@ const documents: Documents = {
"\n mutation BugAddCommentAndReopen($input: BugAddCommentAndReopenInput!) {\n bugAddCommentAndReopen(input: $input) {\n bug {\n id\n }\n }\n }\n": types.BugAddCommentAndReopenDocument,
"\n mutation BugStatusOpen($input: BugStatusOpenInput!) {\n bugStatusOpen(input: $input) {\n bug {\n id\n status\n }\n }\n }\n": types.BugStatusOpenDocument,
"\n mutation BugStatusClose($input: BugStatusCloseInput!) {\n bugStatusClose(input: $input) {\n bug {\n id\n status\n }\n }\n }\n": types.BugStatusCloseDocument,
- "\n mutation BugChangeLabels($input: BugChangeLabelInput) {\n bugChangeLabels(input: $input) {\n bug {\n id\n labels {\n name\n color {\n R\n G\n B\n }\n }\n }\n }\n }\n": types.BugChangeLabelsDocument,
+ "\n mutation BugChangeLabels($input: BugChangeLabelInput) {\n bugChangeLabels(input: $input) {\n bug {\n id\n labels {\n name\n ...LabelFields\n }\n }\n }\n }\n": types.BugChangeLabelsDocument,
"\n fragment BugCreateCommentFields on BugCreateTimelineItem {\n author {\n ...IdentitySummary\n }\n message\n createdAt\n lastEdit\n edited\n }\n": types.BugCreateCommentFieldsFragmentDoc,
"\n fragment BugAddCommentFields on BugAddCommentTimelineItem {\n author {\n ...IdentitySummary\n }\n message\n createdAt\n lastEdit\n edited\n }\n": types.BugAddCommentFieldsFragmentDoc,
"\n fragment LabelChangeFields on BugLabelChangeTimelineItem {\n author {\n humanId\n displayName\n }\n date\n added {\n ...LabelFields\n }\n removed {\n ...LabelFields\n }\n }\n": types.LabelChangeFieldsFragmentDoc,
@@ -64,7 +64,7 @@ const documents: Documents = {
"\n query CommitList($repo: String, $ref: String!, $path: String, $after: String, $first: Int) {\n repository(ref: $repo) {\n commits(ref: $ref, path: $path, after: $after, first: $first) {\n nodes {\n hash\n shortHash\n message\n authorName\n date\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }\n": types.CommitListDocument,
"\n query FileDiff($repo: String, $hash: String!, $path: String!) {\n repository(ref: $repo) {\n commit(hash: $hash) {\n diff(path: $path) {\n path\n oldPath\n isBinary\n isNew\n isDelete\n hunks {\n oldStart\n oldLines\n newStart\n newLines\n lines {\n type\n content\n oldLine\n newLine\n }\n }\n }\n }\n }\n }\n": types.FileDiffDocument,
"\n fragment IdentitySummary on Identity {\n id\n humanId\n displayName\n avatarUrl\n }\n": types.IdentitySummaryFragmentDoc,
- "\n fragment BugSummary on Bug {\n id\n humanId\n status\n title\n labels {\n ...LabelFields\n }\n author {\n ...IdentitySummary\n }\n createdAt\n comments {\n totalCount\n }\n }\n": types.BugSummaryFragmentDoc,
+ "\n fragment BugSummary on Bug {\n id\n humanId\n status\n title\n labels {\n name\n ...LabelFields\n }\n author {\n ...IdentitySummary\n }\n createdAt\n comments {\n totalCount\n }\n }\n": types.BugSummaryFragmentDoc,
"\n fragment LabelFields on Label {\n name\n color {\n R\n G\n B\n }\n }\n": types.LabelFieldsFragmentDoc,
"\n query UserIdentity {\n repository {\n userIdentity {\n id\n humanId\n name\n displayName\n avatarUrl\n email\n login\n }\n }\n }\n": types.UserIdentityDocument,
"\n query CodePageRefs($repo: String) {\n repository(ref: $repo) {\n name\n head {\n shortName\n }\n refs {\n nodes {\n name\n shortName\n type\n hash\n }\n }\n }\n }\n": types.CodePageRefsDocument,
@@ -73,7 +73,7 @@ const documents: Documents = {
"\n query CodePageLastCommits(\n $repo: String\n $ref: String!\n $path: String\n $names: [String!]!\n ) {\n repository(ref: $repo) {\n lastCommits(ref: $ref, path: $path, names: $names) {\n name\n commit {\n hash\n shortHash\n message\n date\n }\n }\n }\n }\n": types.CodePageLastCommitsDocument,
"\n query CodePageReadme($repo: String, $ref: String!, $path: String!) {\n repository(ref: $repo) {\n blob(ref: $ref, path: $path) {\n text\n }\n }\n }\n": types.CodePageReadmeDocument,
"\n query AllIdentities($ref: String) {\n repository(ref: $ref) {\n allIdentities(first: 1000) {\n nodes {\n id\n humanId\n name\n email\n login\n displayName\n avatarUrl\n }\n }\n }\n }\n": types.AllIdentitiesDocument,
- "\n query ValidLabels($ref: String) {\n repository(ref: $ref) {\n validLabels {\n nodes {\n name\n color {\n R\n G\n B\n }\n }\n }\n }\n }\n": types.ValidLabelsDocument,
+ "\n query ValidLabels($ref: String) {\n repository(ref: $ref) {\n validLabels {\n nodes {\n name\n color {\n R\n G\n B\n }\n ...LabelFields\n }\n }\n }\n }\n": types.ValidLabelsDocument,
"\n query BugDetail($ref: String, $prefix: String!) {\n repository(ref: $ref) {\n bug(prefix: $prefix) {\n ...BugSummary\n lastEdit\n participants(first: 20) {\n nodes {\n ...IdentitySummary\n }\n }\n timeline(first: 250) {\n nodes {\n __typename\n id\n ... on BugCreateTimelineItem {\n ...BugCreateCommentFields\n }\n ... on BugAddCommentTimelineItem {\n ...BugAddCommentFields\n }\n ... on BugLabelChangeTimelineItem {\n ...LabelChangeFields\n }\n ... on BugSetStatusTimelineItem {\n ...StatusChangeFields\n }\n ... on BugSetTitleTimelineItem {\n ...TitleChangeFields\n }\n }\n }\n }\n }\n }\n": types.BugDetailDocument,
"\n query BugList(\n $ref: String\n $openQuery: String!\n $closedQuery: String!\n $listQuery: String!\n $first: Int\n $after: String\n ) {\n repository(ref: $ref) {\n openCount: allBugs(query: $openQuery, first: 1) {\n totalCount\n }\n closedCount: allBugs(query: $closedQuery, first: 1) {\n totalCount\n }\n bugs: allBugs(query: $listQuery, first: $first, after: $after) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n ...BugSummary\n }\n }\n }\n }\n": types.BugListDocument,
"\n mutation BugCreate($input: BugCreateInput!) {\n bugCreate(input: $input) {\n bug {\n id\n humanId\n }\n }\n }\n": types.BugCreateDocument,
@@ -119,7 +119,7 @@ export function graphql(source: "\n mutation BugStatusClose($input: BugStatusCl
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
-export function graphql(source: "\n mutation BugChangeLabels($input: BugChangeLabelInput) {\n bugChangeLabels(input: $input) {\n bug {\n id\n labels {\n name\n color {\n R\n G\n B\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation BugChangeLabels($input: BugChangeLabelInput) {\n bugChangeLabels(input: $input) {\n bug {\n id\n labels {\n name\n color {\n R\n G\n B\n }\n }\n }\n }\n }\n"];
+export function graphql(source: "\n mutation BugChangeLabels($input: BugChangeLabelInput) {\n bugChangeLabels(input: $input) {\n bug {\n id\n labels {\n name\n ...LabelFields\n }\n }\n }\n }\n"): (typeof documents)["\n mutation BugChangeLabels($input: BugChangeLabelInput) {\n bugChangeLabels(input: $input) {\n bug {\n id\n labels {\n name\n ...LabelFields\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -163,7 +163,7 @@ export function graphql(source: "\n fragment IdentitySummary on Identity {\n
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
-export function graphql(source: "\n fragment BugSummary on Bug {\n id\n humanId\n status\n title\n labels {\n ...LabelFields\n }\n author {\n ...IdentitySummary\n }\n createdAt\n comments {\n totalCount\n }\n }\n"): (typeof documents)["\n fragment BugSummary on Bug {\n id\n humanId\n status\n title\n labels {\n ...LabelFields\n }\n author {\n ...IdentitySummary\n }\n createdAt\n comments {\n totalCount\n }\n }\n"];
+export function graphql(source: "\n fragment BugSummary on Bug {\n id\n humanId\n status\n title\n labels {\n name\n ...LabelFields\n }\n author {\n ...IdentitySummary\n }\n createdAt\n comments {\n totalCount\n }\n }\n"): (typeof documents)["\n fragment BugSummary on Bug {\n id\n humanId\n status\n title\n labels {\n name\n ...LabelFields\n }\n author {\n ...IdentitySummary\n }\n createdAt\n comments {\n totalCount\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -199,7 +199,7 @@ export function graphql(source: "\n query AllIdentities($ref: String) {\n re
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
-export function graphql(source: "\n query ValidLabels($ref: String) {\n repository(ref: $ref) {\n validLabels {\n nodes {\n name\n color {\n R\n G\n B\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query ValidLabels($ref: String) {\n repository(ref: $ref) {\n validLabels {\n nodes {\n name\n color {\n R\n G\n B\n }\n }\n }\n }\n }\n"];
+export function graphql(source: "\n query ValidLabels($ref: String) {\n repository(ref: $ref) {\n validLabels {\n nodes {\n name\n color {\n R\n G\n B\n }\n ...LabelFields\n }\n }\n }\n }\n"): (typeof documents)["\n query ValidLabels($ref: String) {\n repository(ref: $ref) {\n validLabels {\n nodes {\n name\n color {\n R\n G\n B\n }\n ...LabelFields\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1379,13 +1379,13 @@ export const LabelFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":
@@ -120,7 +120,7 @@ export function CommentBox({ bugPrefix, bugStatus, ref_ }: CommentBoxProps) {
return (
<CommentCard.Root>
- <CommentCard.AuthorAvatar avatarUrl={user.avatarUrl} displayName={user.displayName} />
+ <CommentCard.AuthorAvatar from={user} />
<CommentCard.Card>
<WritePreview.Root hasContent={hasMessage} preview={preview} onPreviewChange={setPreview}>
<WritePreview.Tabs className="border-border border-b px-4 py-2" />
@@ -14,10 +14,11 @@ import {
import { Settings2 } from "lucide-react";
import { useRef, useState } from "react";
+import type { FragmentType } from "@apollo/client/masking";
import { BugDetailDocument } from "@/__generated__/graphql";
import { graphql } from "@/__generated__/gql";
import * as Listbox from "@/components/ui/listbox";
-import { LabelBadge } from "@/components/shared/label-badge";
+import { LabelBadge, LABEL_FIELDS_FRAGMENT } from "@/components/shared/label-badge";
import { SectionHeading } from "@/components/shared/section-heading";
import { useAuth } from "@/lib/auth";
@@ -28,11 +29,7 @@ const BUG_CHANGE_LABELS_MUTATION = graphql(`
id
labels {
name
- color {
- R
- G
- B
- }
+ ...LabelFields
}
}
}
@@ -41,11 +38,11 @@ const BUG_CHANGE_LABELS_MUTATION = graphql(`
interface LabelEditorProps {
bugPrefix: string;
- currentLabels: Array<{ name: string; color: { R: number; G: number; B: number } }>;
+ currentLabels: Array<{ name: string } & FragmentType<typeof LABEL_FIELDS_FRAGMENT>>;
/** Current repo slug, passed as `ref` in refetch query variables. */
ref_?: string | null;
/** Pre-fetched valid labels for the repository. */
- validLabels: Array<{ name: string; color: { R: number; G: number; B: number } }>;
+ validLabels: Array<{ name: string; color: { R: number; G: number; B: number } } & FragmentType<typeof LABEL_FIELDS_FRAGMENT>>;
}
// Gear-icon popover in the BugDetailPage sidebar for adding/removing labels.
@@ -160,7 +157,7 @@ export function LabelEditor({ bugPrefix, currentLabels, ref_, validLabels }: Lab
: {}
}
/>
- <LabelBadge name={label.name} color={label.color} />
+ <LabelBadge from={label} />
</Listbox.Item>
);
})}
@@ -175,7 +172,7 @@ export function LabelEditor({ bugPrefix, currentLabels, ref_, validLabels }: Lab
) : (
<div className="flex flex-wrap gap-1">
{currentLabels.map((label) => (
- <LabelBadge key={label.name} name={label.name} color={label.color} />
+ <LabelBadge key={label.name} from={label} />
))}
</div>
)}
@@ -13,7 +13,7 @@ import { LabelBadge } from "@/components/shared/label-badge";
import { Textarea } from "@/components/ui/textarea";
import { useAuth } from "@/lib/auth";
-const BUG_CREATE_COMMENT_FIELDS = graphql(`
+graphql(`
fragment BugCreateCommentFields on BugCreateTimelineItem {
author {
...IdentitySummary
@@ -25,7 +25,7 @@ const BUG_CREATE_COMMENT_FIELDS = graphql(`
}
`);
-const BUG_ADD_COMMENT_FIELDS = graphql(`
+graphql(`
fragment BugAddCommentFields on BugAddCommentTimelineItem {
author {
...IdentitySummary
@@ -37,7 +37,7 @@ const BUG_ADD_COMMENT_FIELDS = graphql(`
}
`);
-const LABEL_CHANGE_FIELDS = graphql(`
+graphql(`
fragment LabelChangeFields on BugLabelChangeTimelineItem {
author {
humanId
@@ -53,7 +53,7 @@ const LABEL_CHANGE_FIELDS = graphql(`
}
`);
-const STATUS_CHANGE_FIELDS = graphql(`
+graphql(`
fragment StatusChangeFields on BugSetStatusTimelineItem {
author {
humanId
@@ -64,7 +64,7 @@ const STATUS_CHANGE_FIELDS = graphql(`
}
`);
-const TITLE_CHANGE_FIELDS = graphql(`
+graphql(`
fragment TitleChangeFields on BugSetTitleTimelineItem {
author {
humanId
@@ -164,7 +164,7 @@ function CommentItem({
return (
<CommentCard.Root>
- <CommentCard.AuthorAvatar avatarUrl={item.author.avatarUrl} displayName={item.author.displayName} />
+ <CommentCard.AuthorAvatar from={item.author} />
<CommentCard.Card>
<CommentCard.CardHeader>
<Link
@@ -257,16 +257,16 @@ function LabelChangeItem({ item, repo }: { item: LabelChangeItem; repo: string |
{item.added.length > 0 && (
<>
added{" "}
- {item.added.map((l) => (
- <LabelBadge key={l.name} name={l.name} color={l.color} />
+ {item.added.map((l, i) => (
+ <LabelBadge key={i} from={l} />
))}{" "}
</>
)}
{item.removed.length > 0 && (
<>
removed{" "}
- {item.removed.map((l) => (
- <LabelBadge key={l.name} name={l.name} color={l.color} />
+ {item.removed.map((l, i) => (
+ <LabelBadge key={i} from={l} />
))}{" "}
</>
)}
@@ -1,10 +1,12 @@
-import type { IdentitySummaryFragment } from "@/__generated__/graphql";
+import { useSuspenseFragment } from "@apollo/client/react";
+import type { FragmentType } from "@apollo/client/masking";
+
import { graphql } from "@/__generated__/gql";
import { cn } from "@/lib/utils";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
-const IDENTITY_SUMMARY_FRAGMENT = graphql(`
+export const IDENTITY_SUMMARY_FRAGMENT = graphql(`
fragment IdentitySummary on Identity {
id
humanId
@@ -22,16 +24,19 @@ export function Root({ children, className }: RootProps) {
return <div className={cn("flex gap-3", className)}>{children}</div>;
}
-type AuthorAvatarProps = Pick<IdentitySummaryFragment, "displayName" | "avatarUrl"> & {
+interface AuthorAvatarProps {
+ from: FragmentType<typeof IDENTITY_SUMMARY_FRAGMENT>;
className?: string;
-};
+}
+
+export function AuthorAvatar({ from, className }: AuthorAvatarProps) {
+ const { data } = useSuspenseFragment({ fragment: IDENTITY_SUMMARY_FRAGMENT, from });
-export function AuthorAvatar({ avatarUrl, displayName, className }: AuthorAvatarProps) {
return (
<Avatar className={cn("mt-1 size-8 shrink-0", className)}>
- <AvatarImage src={avatarUrl ?? undefined} alt={displayName} />
+ <AvatarImage src={data.avatarUrl ?? undefined} alt={data.displayName} />
<AvatarFallback className="text-xs">
- {displayName.slice(0, 2).toUpperCase()}
+ {data.displayName.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
);
@@ -14,13 +14,14 @@ import {
import { ArrowUpDown, ChevronDown, Tag, User, X } from "lucide-react";
import { useMemo, useRef, useState, useCallback, useEffect } from "react";
+import type { FragmentType } from "@apollo/client/masking";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import * as Listbox from "@/components/ui/listbox";
import { useAuth } from "@/lib/auth";
import { SORT_OPTIONS, type SortValue } from "@/lib/query-utils";
import { cn } from "@/lib/utils";
-import { LabelBadge } from "@/components/shared/label-badge";
+import { LabelBadge, LABEL_FIELDS_FRAGMENT } from "@/components/shared/label-badge";
// Max authors shown in the non-searching state. We intentionally cap this to
// avoid a giant list — the current-user + recently-seen pattern covers the
@@ -46,10 +47,10 @@ function authorQueryValue(i: {
export type { SortValue } from "@/lib/query-utils";
-export interface LabelItem {
+export type LabelItem = {
name: string;
color: { R: number; G: number; B: number };
-}
+} & FragmentType<typeof LABEL_FIELDS_FRAGMENT>;
export interface IdentityItem {
id: string;
@@ -372,7 +373,7 @@ function LabelFilter({
opacity: active ? 1 : 0.35,
}}
/>
- <LabelBadge name={label.name} color={label.color} />
+ <LabelBadge from={label} />
</Listbox.Item>
);
})}
@@ -4,13 +4,14 @@ import { Status } from "@/__generated__/graphql";
import { graphql } from "@/__generated__/gql";
import { cn } from "@/lib/utils";
-const BUG_SUMMARY_FRAGMENT = graphql(`
+graphql(`
fragment BugSummary on Bug {
id
humanId
status
title
labels {
+ name
...LabelFields
}
author {
@@ -1,10 +1,11 @@
+import { useSuspenseFragment } from "@apollo/client/react";
+import type { FragmentType } from "@apollo/client/masking";
import { createLink, type LinkComponent } from "@tanstack/react-router";
import * as React from "react";
-import type { LabelFieldsFragment } from "@/__generated__/graphql";
import { graphql } from "@/__generated__/gql";
-const LABEL_FIELDS_FRAGMENT = graphql(`
+export const LABEL_FIELDS_FRAGMENT = graphql(`
fragment LabelFields on Label {
name
color {
@@ -15,24 +16,25 @@ const LABEL_FIELDS_FRAGMENT = graphql(`
}
`);
-type LabelBadgeProps = LabelFieldsFragment & {
- className?: string;
-};
-
function contrastColor(r: number, g: number, b: number): string {
- // Perceived luminance — pick black or white text for readability
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
return luminance > 0.55 ? "rgba(0,0,0,0.75)" : "rgba(255,255,255,0.9)";
}
+interface LabelBadgeProps {
+ from: FragmentType<typeof LABEL_FIELDS_FRAGMENT>;
+ className?: string;
+}
+
// Coloured label pill. Always renders as a <span>.
// Use LabelBadgeLink for a clickable variant that navigates.
const LabelBadge = React.forwardRef<
HTMLSpanElement,
LabelBadgeProps & Omit<React.HTMLAttributes<HTMLSpanElement>, "color">
->(({ name, color, className, ...props }, ref) => {
- const bg = `rgb(${color.R},${color.G},${color.B})`;
- const text = contrastColor(color.R, color.G, color.B);
+>(({ from, className, ...props }, ref) => {
+ const { data } = useSuspenseFragment({ fragment: LABEL_FIELDS_FRAGMENT, from });
+ const bg = `rgb(${data.color.R},${data.color.G},${data.color.B})`;
+ const text = contrastColor(data.color.R, data.color.G, data.color.B);
return (
<span
@@ -41,7 +43,7 @@ const LabelBadge = React.forwardRef<
style={{ backgroundColor: bg, color: text }}
{...props}
>
- {name}
+ {data.name}
</span>
);
});
@@ -52,9 +54,10 @@ const CreatedLabelBadgeLink = createLink(
React.forwardRef<
HTMLAnchorElement,
LabelBadgeProps & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "color">
- >(({ name, color, className, ...props }, ref) => {
- const bg = `rgb(${color.R},${color.G},${color.B})`;
- const text = contrastColor(color.R, color.G, color.B);
+ >(({ from, className, ...props }, ref) => {
+ const { data } = useSuspenseFragment({ fragment: LABEL_FIELDS_FRAGMENT, from });
+ const bg = `rgb(${data.color.R},${data.color.G},${data.color.B})`;
+ const text = contrastColor(data.color.R, data.color.G, data.color.B);
return (
<a
@@ -63,7 +66,7 @@ const CreatedLabelBadgeLink = createLink(
style={{ backgroundColor: bg, color: text }}
{...props}
>
- {name}
+ {data.name}
</a>
);
}),
@@ -9,6 +9,7 @@ const httpLink = new HttpLink({
export const client = new ApolloClient({
link: httpLink,
+ dataMasking: true,
cache: new InMemoryCache({
typePolicies: {
@@ -31,6 +31,7 @@ const VALID_LABELS_QUERY = graphql(`
G
B
}
+ ...LabelFields
}
}
}
@@ -290,8 +290,7 @@ function RouteComponent() {
{bug.labels.map((label) => (
<LabelBadgeLink
key={label.name}
- name={label.name}
- color={label.color}
+ from={label}
to="/$repo/issues"
params={{ repo }}
search={{
@@ -191,7 +191,7 @@ function RouteComponent() {
{bug.title}
</Link>
{bug.labels.map((label) => (
- <LabelBadge key={label.name} name={label.name} color={label.color} />
+ <LabelBadge key={label.name} from={label} />
))}
</IssueRow.TitleArea>
<IssueRow.Meta>