From 8bb7f67e87c8845524ecaa56346a6ce32e3bbbb3 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Thu, 9 Apr 2026 00:24:33 +0200 Subject: [PATCH] refactor(web): add GitRefFields fragment for ref-selector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define a GitRefFields fragment in ref-selector.tsx and use it in the CodePageRefs query. The RefSelector takes unmasked fragment data directly (no useSuspenseFragment needed — it's a self-contained UI component) and its onSelect callback returns the shortName string. Co-Authored-By: Claude Opus 4.6 (1M context) --- webui2/src/__generated__/gql.ts | 12 +- webui2/src/__generated__/graphql.ts | 7 +- webui2/src/components/code/ref-selector.tsx | 119 +++++++++----------- webui2/src/routes/$repo.tsx | 12 +- webui2/src/routes/$repo/_code.tsx | 8 +- 5 files changed, 70 insertions(+), 88 deletions(-) diff --git a/webui2/src/__generated__/gql.ts b/webui2/src/__generated__/gql.ts index 834983a9962c792ea112ca7daafb63e5d1f25524..731f3effd0e99a83444c102b1601dd383d556b00 100644 --- a/webui2/src/__generated__/gql.ts +++ b/webui2/src/__generated__/gql.ts @@ -29,11 +29,12 @@ type Documents = { "\n mutation BugSetTitle($input: BugSetTitleInput!) {\n bugSetTitle(input: $input) {\n bug {\n id\n title\n }\n }\n }\n": typeof types.BugSetTitleDocument, "\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 RefSelectorRefs on GitRefConnection {\n nodes {\n name\n shortName\n type\n }\n }\n": typeof types.RefSelectorRefsFragmentDoc, "\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 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, + "\n query CodePageRefs($repo: String) {\n repository(ref: $repo) {\n name\n head {\n shortName\n }\n refs {\n ...RefSelectorRefs\n }\n }\n }\n": typeof types.CodePageRefsDocument, "\n query CodePageBlob($repo: String, $ref: String!, $path: String!) {\n repository(ref: $repo) {\n blob(ref: $ref, path: $path) {\n path\n hash\n text\n size\n isBinary\n isTruncated\n }\n }\n }\n": typeof types.CodePageBlobDocument, "\n query CodePageTree($repo: String, $ref: String!, $path: String) {\n repository(ref: $repo) {\n tree(ref: $ref, path: $path) {\n name\n type\n hash\n }\n }\n }\n": typeof types.CodePageTreeDocument, "\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, @@ -63,11 +64,12 @@ const documents: Documents = { "\n mutation BugSetTitle($input: BugSetTitleInput!) {\n bugSetTitle(input: $input) {\n bug {\n id\n title\n }\n }\n }\n": types.BugSetTitleDocument, "\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 RefSelectorRefs on GitRefConnection {\n nodes {\n name\n shortName\n type\n }\n }\n": types.RefSelectorRefsFragmentDoc, "\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 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, + "\n query CodePageRefs($repo: String) {\n repository(ref: $repo) {\n name\n head {\n shortName\n }\n refs {\n ...RefSelectorRefs\n }\n }\n }\n": types.CodePageRefsDocument, "\n query CodePageBlob($repo: String, $ref: String!, $path: String!) {\n repository(ref: $repo) {\n blob(ref: $ref, path: $path) {\n path\n hash\n text\n size\n isBinary\n isTruncated\n }\n }\n }\n": types.CodePageBlobDocument, "\n query CodePageTree($repo: String, $ref: String!, $path: String) {\n repository(ref: $repo) {\n tree(ref: $ref, path: $path) {\n name\n type\n hash\n }\n }\n }\n": types.CodePageTreeDocument, "\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, @@ -156,6 +158,10 @@ export function graphql(source: "\n query CommitList($repo: String, $ref: Strin * 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 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 documents)["\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"]; +/** + * 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 RefSelectorRefs on GitRefConnection {\n nodes {\n name\n shortName\n type\n }\n }\n"): (typeof documents)["\n fragment RefSelectorRefs on GitRefConnection {\n nodes {\n name\n shortName\n type\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -175,7 +181,7 @@ export function graphql(source: "\n query UserIdentity {\n repository {\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 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 documents)["\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"]; +export function graphql(source: "\n query CodePageRefs($repo: String) {\n repository(ref: $repo) {\n name\n head {\n shortName\n }\n refs {\n ...RefSelectorRefs\n }\n }\n }\n"): (typeof documents)["\n query CodePageRefs($repo: String) {\n repository(ref: $repo) {\n name\n head {\n shortName\n }\n refs {\n ...RefSelectorRefs\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/webui2/src/__generated__/graphql.ts b/webui2/src/__generated__/graphql.ts index 6d1a14734d1d681972e8aa9a4c539038bf12d693..8f8cafb36691a0225711a7a43abfd6caea7e22ad 100644 --- a/webui2/src/__generated__/graphql.ts +++ b/webui2/src/__generated__/graphql.ts @@ -1245,6 +1245,8 @@ export type FileDiffQueryVariables = Exact<{ export type FileDiffQuery = { repository: { __typename: 'Repository', commit: { __typename: 'GitCommit', diff: { __typename: 'GitFileDiff', path: string, oldPath: string | null, isBinary: boolean, isNew: boolean, isDelete: boolean, hunks: Array<{ __typename: 'GitDiffHunk', oldStart: number, oldLines: number, newStart: number, newLines: number, lines: Array<{ __typename: 'GitDiffLine', type: GitDiffLineType, content: string, oldLine: number, newLine: number }> }> } | null } | null } | null }; +export type RefSelectorRefsFragment = { __typename: 'GitRefConnection', nodes: Array<{ __typename: 'GitRef', name: string, shortName: string, type: GitRefType }> }; + export type IdentitySummaryFragment = { __typename: 'Identity', id: string, humanId: string, displayName: string, avatarUrl: string | null }; export type BugSummaryFragment = { __typename: 'Bug', id: string, humanId: string, status: Status, title: string, createdAt: string, labels: Array<{ __typename: 'Label', name: string, color: { __typename: 'Color', R: number, G: number, B: number } }>, author: { __typename: 'Identity', id: string, humanId: string, displayName: string, avatarUrl: string | null }, comments: { __typename: 'BugCommentConnection', totalCount: number } }; @@ -1261,7 +1263,7 @@ export type CodePageRefsQueryVariables = Exact<{ }>; -export type CodePageRefsQuery = { repository: { __typename: 'Repository', name: string | null, head: { __typename: 'GitRef', shortName: string } | null, refs: { __typename: 'GitRefConnection', nodes: Array<{ __typename: 'GitRef', name: string, shortName: string, type: GitRefType, hash: string }> } } | null }; +export type CodePageRefsQuery = { repository: { __typename: 'Repository', name: string | null, head: { __typename: 'GitRef', shortName: string } | null, refs: { __typename: 'GitRefConnection', nodes: Array<{ __typename: 'GitRef', name: string, shortName: string, type: GitRefType }> } } | null }; export type CodePageBlobQueryVariables = Exact<{ repo?: InputMaybe; @@ -1379,6 +1381,7 @@ export const LabelFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind": export const LabelChangeFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LabelChangeFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BugLabelChangeTimelineItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"humanId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"added"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LabelFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LabelFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LabelFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Label"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"R"}},{"kind":"Field","name":{"kind":"Name","value":"G"}},{"kind":"Field","name":{"kind":"Name","value":"B"}}]}}]}}]} as unknown as DocumentNode; export const StatusChangeFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StatusChangeFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BugSetStatusTimelineItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"humanId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]} as unknown as DocumentNode; export const TitleChangeFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TitleChangeFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BugSetTitleTimelineItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"humanId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"was"}}]}}]} as unknown as DocumentNode; +export const RefSelectorRefsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RefSelectorRefs"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GitRefConnection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"shortName"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]} as unknown as DocumentNode; export const BugSummaryFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BugSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Bug"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"humanId"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"labels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LabelFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"IdentitySummary"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LabelFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Label"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"R"}},{"kind":"Field","name":{"kind":"Name","value":"G"}},{"kind":"Field","name":{"kind":"Name","value":"B"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"IdentitySummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Identity"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"humanId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}}]} as unknown as DocumentNode; export const BugAddCommentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"BugAddComment"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"BugAddCommentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bugAddComment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bug"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const BugAddCommentAndCloseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"BugAddCommentAndClose"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"BugAddCommentAndCloseInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bugAddCommentAndClose"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bug"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -1391,7 +1394,7 @@ export const BugSetTitleDocument = {"kind":"Document","definitions":[{"kind":"Op export const CommitListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CommitList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"repo"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ref"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"repo"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ref"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"shortHash"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const FileDiffDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FileDiff"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"repo"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"hash"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"repo"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"hash"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hash"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"diff"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"oldPath"}},{"kind":"Field","name":{"kind":"Name","value":"isBinary"}},{"kind":"Field","name":{"kind":"Name","value":"isNew"}},{"kind":"Field","name":{"kind":"Name","value":"isDelete"}},{"kind":"Field","name":{"kind":"Name","value":"hunks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oldStart"}},{"kind":"Field","name":{"kind":"Name","value":"oldLines"}},{"kind":"Field","name":{"kind":"Name","value":"newStart"}},{"kind":"Field","name":{"kind":"Name","value":"newLines"}},{"kind":"Field","name":{"kind":"Name","value":"lines"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"oldLine"}},{"kind":"Field","name":{"kind":"Name","value":"newLine"}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const UserIdentityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserIdentity"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userIdentity"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"humanId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"login"}}]}}]}}]}}]} as unknown as DocumentNode; -export const CodePageRefsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CodePageRefs"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"repo"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"repo"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"head"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"shortName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"refs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"shortName"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const CodePageRefsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CodePageRefs"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"repo"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"repo"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"head"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"shortName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"refs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RefSelectorRefs"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RefSelectorRefs"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GitRefConnection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"shortName"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]} as unknown as DocumentNode; export const CodePageBlobDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CodePageBlob"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"repo"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ref"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"repo"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blob"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ref"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"isBinary"}},{"kind":"Field","name":{"kind":"Name","value":"isTruncated"}}]}}]}}]}}]} as unknown as DocumentNode; export const CodePageTreeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CodePageTree"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"repo"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ref"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"repo"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tree"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ref"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}}]}}]}}]}}]} as unknown as DocumentNode; export const CodePageLastCommitsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CodePageLastCommits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"repo"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ref"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"names"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"repository"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"repo"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lastCommits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ref"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ref"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"names"},"value":{"kind":"Variable","name":{"kind":"Name","value":"names"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"commit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"shortHash"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/webui2/src/components/code/ref-selector.tsx b/webui2/src/components/code/ref-selector.tsx index 64b1bfb931ae0b8c63175586606e6596f18dd39a..931fb7e55e8f4203b48050444191a48e98e036c2 100644 --- a/webui2/src/components/code/ref-selector.tsx +++ b/webui2/src/components/code/ref-selector.tsx @@ -1,3 +1,5 @@ +import { useSuspenseFragment } from "@apollo/client/react"; +import type { FragmentType } from "@apollo/client/masking"; import { useFloating, useClick, @@ -14,20 +16,33 @@ import { GitBranch, Tag } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { GitRefType } from "@/__generated__/graphql"; -import type { RefsQueryRef } from "@/routes/$repo"; +import { graphql } from "@/__generated__/gql"; import { Button } from "@/components/ui/button"; import * as Listbox from "@/components/ui/listbox"; import { cn } from "@/lib/utils"; +export const REF_SELECTOR_REFS_FRAGMENT = graphql(` + fragment RefSelectorRefs on GitRefConnection { + nodes { + name + shortName + type + } + } +`); + interface RefSelectorProps { - gitRefs: RefsQueryRef[]; + refs: FragmentType; currentRef: string; - onSelect: (ref: RefsQueryRef) => void; + onSelect: (shortName: string) => void; } // Branch / tag selector dropdown for the code browser. Shown in two groups // (branches, tags) with an inline search filter. -export function RefSelector({ gitRefs, currentRef, onSelect }: RefSelectorProps) { +export function RefSelector({ refs: refsProp, currentRef, onSelect }: RefSelectorProps) { + const { data } = useSuspenseFragment({ fragment: REF_SELECTOR_REFS_FRAGMENT, from: refsProp }); + const gitRefs = data.nodes; + const [open, setOpen] = useState(false); const [filter, setFilter] = useState(""); const [activeIndex, setActiveIndex] = useState(null); @@ -84,7 +99,7 @@ export function RefSelector({ gitRefs, currentRef, onSelect }: RefSelectorProps) e.preventDefault(); const ref = flatItems[activeIndex]; if (ref) { - onSelect(ref); + onSelect(ref.shortName); setOpen(false); setFilter(""); } @@ -136,21 +151,26 @@ export function RefSelector({ gitRefs, currentRef, onSelect }: RefSelectorProps) {branches.map((ref) => { const i = itemIndex++; return ( - { + elementsRef.current[i] = el; + }} active={activeIndex === i} selected={ref.shortName === currentRef} - elementsRef={elementsRef} - getItemProps={getItemProps} - onSelect={() => { - onSelect(ref); - setOpen(false); - setFilter(""); - }} - /> + className={cn("text-xs", ref.shortName === currentRef && "font-medium")} + {...getItemProps({ + onClick: () => { + onSelect(ref.shortName); + setOpen(false); + setFilter(""); + }, + })} + > + + {ref.shortName} + ); })} @@ -161,21 +181,26 @@ export function RefSelector({ gitRefs, currentRef, onSelect }: RefSelectorProps) {tags.map((ref) => { const i = itemIndex++; return ( - { + elementsRef.current[i] = el; + }} active={activeIndex === i} selected={ref.shortName === currentRef} - elementsRef={elementsRef} - getItemProps={getItemProps} - onSelect={() => { - onSelect(ref); - setOpen(false); - setFilter(""); - }} - /> + className={cn("text-xs", ref.shortName === currentRef && "font-medium")} + {...getItemProps({ + onClick: () => { + onSelect(ref.shortName); + setOpen(false); + setFilter(""); + }, + })} + > + + {ref.shortName} + ); })} @@ -189,43 +214,3 @@ export function RefSelector({ gitRefs, currentRef, onSelect }: RefSelectorProps) ); } - -function RefItem({ - id, - ref_, - index, - active, - selected, - elementsRef, - getItemProps, - onSelect, -}: { - id: string; - ref_: RefsQueryRef; - index: number; - active: boolean; - selected: boolean; - elementsRef: React.MutableRefObject<(HTMLElement | null)[]>; - getItemProps: (props?: Record) => Record; - onSelect: () => void; -}) { - return ( - { - elementsRef.current[index] = el; - }} - active={active} - selected={selected} - className={cn("text-xs", selected && "font-medium")} - {...getItemProps({ onClick: onSelect })} - > - {ref_.type === GitRefType.Branch ? ( - - ) : ( - - )} - {ref_.shortName} - - ); -} diff --git a/webui2/src/routes/$repo.tsx b/webui2/src/routes/$repo.tsx index 0064e3110a74e668724d16990fa52567f4113c9a..31347ac183ecabe7cb3abb670b28194dd8e40d6a 100644 --- a/webui2/src/routes/$repo.tsx +++ b/webui2/src/routes/$repo.tsx @@ -11,12 +11,7 @@ export const REFS_QUERY = graphql(` shortName } refs { - nodes { - name - shortName - type - hash - } + ...RefSelectorRefs } } } @@ -24,11 +19,6 @@ export const REFS_QUERY = graphql(` export type RefsQueryData = ResultOf; -/** A single git ref as returned by the REFS_QUERY. */ -export type RefsQueryRef = NonNullable< - NonNullable["refs"] ->["nodes"][number]; - export const Route = createFileRoute("/$repo")({ beforeLoad: ({ params: { repo }, context: { preloadQuery } }) => { // Normalize the repo slug: "_" means the default (null) repo diff --git a/webui2/src/routes/$repo/_code.tsx b/webui2/src/routes/$repo/_code.tsx index e15dd7082e269b61b4b5c7da6c1a71564c7ffb22..0403a03b760cfa8dc57539fb6d7a3d7eca817ca5 100644 --- a/webui2/src/routes/$repo/_code.tsx +++ b/webui2/src/routes/$repo/_code.tsx @@ -12,7 +12,6 @@ import { } from "@tanstack/react-router"; import { GitCommit } from "lucide-react"; -import type { RefsQueryRef } from "@/routes/$repo"; import { CodeBreadcrumb } from "@/components/code/code-breadcrumb"; import { RefSelector } from "@/components/code/ref-selector"; import { ButtonLink } from "@/components/ui/button-link"; @@ -29,7 +28,7 @@ function CodeLayout() { const { repo } = Route.useParams(); const { ref: repoRef, refsRef } = Route.useRouteContext(); const { data: refsData } = useReadQuery(refsRef); - const refs: RefsQueryRef[] = refsData?.repository?.refs?.nodes ?? []; + const refs = refsData?.repository?.refs; const repoName = refsData?.repository?.name ?? repoRef ?? "default-repo"; // Read child route params (ref and splat path) @@ -51,8 +50,7 @@ function CodeLayout() { const navigate = useNavigate(); - function handleRefSelect(newRef: RefsQueryRef) { - const refName = newRef.shortName; + function handleRefSelect(refName: string) { if (viewMode === "commits") { void navigate({ to: "/$repo/commits/$ref", params: { repo, ref: refName }, search: { path: currentPath || undefined } }); } else if (viewMode === "blob") { @@ -100,7 +98,7 @@ function CodeLayout() { History )} - + {refs && }