extract commit list (into TreeCommits) from TreePage (#45883)

TreePage was getting big. There is no behavior or UI change, this is just a refactor.
This commit is contained in:
Quinn Slack 2022-12-21 14:26:43 -08:00 committed by GitHub
parent 33f0bc372f
commit 39703ad1af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 205 additions and 184 deletions

View File

@ -17,19 +17,6 @@
max-width: var(--media-xl);
}
.git-commit-node {
padding-left: 0;
padding-right: 0;
&-message-subject {
opacity: 0.9;
}
:global(.btn) {
opacity: 0.85;
}
}
.container {
overflow-y: auto;
height: 100%;

View File

@ -200,7 +200,7 @@ export const TreePage: React.FunctionComponent<React.PropsWithChildren<Props>> =
}
// To start using the feature flag bellow, you can go to /site-admin/feature-flags and
// create a new featureFlag named 'new-repo-page' and set its value to true.
// create a new featureFlag named 'new-repo-page' and set its to true.
// https://docs.sourcegraph.com/dev/how-to/use_feature_flags#create-a-feature-flag
const [isNewRepoPageEnabled] = useFeatureFlag('new-repo-page')

View File

@ -1,12 +1,9 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import React, { useMemo } from 'react'
import classNames from 'classnames'
import formatISO from 'date-fns/formatISO'
import subYears from 'date-fns/subYears'
import * as H from 'history'
import { ContributableMenu } from '@sourcegraph/client-api'
import { dataOrThrowErrors, gql } from '@sourcegraph/http-client'
import { ActionItem } from '@sourcegraph/shared/src/actions/ActionItem'
import { ActionsContainer } from '@sourcegraph/shared/src/actions/ActionsContainer'
import { FileDecorationsByPath } from '@sourcegraph/shared/src/api/extension/extensionHostApi'
@ -15,62 +12,16 @@ import { TreeFields } from '@sourcegraph/shared/src/graphql-operations'
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
import { ThemeProps } from '@sourcegraph/shared/src/theme'
import { Button, Heading, Link, useObservable, ErrorAlert } from '@sourcegraph/wildcard'
import { Button, Heading, useObservable } from '@sourcegraph/wildcard'
import { getFileDecorations } from '../../backend/features'
import { useShowMorePagination } from '../../components/FilteredConnection/hooks/useShowMorePagination'
import {
ConnectionContainer,
ConnectionList,
ConnectionLoading,
ConnectionSummary,
ShowMoreButton,
SummaryContainer,
} from '../../components/FilteredConnection/ui'
import {
GitCommitFields,
TreeCommitsResult,
TreeCommitsVariables,
TreePageRepositoryFields,
} from '../../graphql-operations'
import { GitCommitNode } from '../commits/GitCommitNode'
import { gitCommitFragment } from '../commits/RepositoryCommitsPage'
import { TreePageRepositoryFields } from '../../graphql-operations'
import { TreeCommits } from './commits/TreeCommits'
import { TreeEntriesSection } from './TreeEntriesSection'
import styles from './TreePage.module.scss'
const TREE_COMMITS_PER_PAGE = 10
const TREE_COMMITS_QUERY = gql`
query TreeCommits(
$repo: ID!
$revspec: String!
$first: Int
$filePath: String
$after: String
$afterCursor: String
) {
node(id: $repo) {
__typename
... on Repository {
commit(rev: $revspec) {
ancestors(first: $first, path: $filePath, after: $after, afterCursor: $afterCursor) {
nodes {
...GitCommitFields
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
}
${gitCommitFragment}
`
interface TreePageContentProps extends ExtensionsControllerProps, ThemeProps, TelemetryProps, PlatformContextProps {
filePath: string
tree: TreeFields
@ -88,62 +39,6 @@ export const TreePageContent: React.FunctionComponent<React.PropsWithChildren<Tr
revision,
...props
}) => {
const [showOlderCommits, setShowOlderCommits] = useState(false)
const after = useMemo(() => (showOlderCommits ? null : formatISO(subYears(Date.now(), 1))), [showOlderCommits])
const { connection, error, loading, hasNextPage, fetchMore, refetchAll } = useShowMorePagination<
TreeCommitsResult,
TreeCommitsVariables,
GitCommitFields
>({
query: TREE_COMMITS_QUERY,
variables: {
repo: repo.id,
revspec: revision || '',
first: TREE_COMMITS_PER_PAGE,
filePath,
after,
afterCursor: null,
},
getConnection: result => {
const { node } = dataOrThrowErrors(result)
if (!node) {
return { nodes: [] }
}
if (node.__typename !== 'Repository') {
return { nodes: [] }
}
if (!node.commit?.ancestors) {
return { nodes: [] }
}
return node.commit.ancestors
},
options: {
fetchPolicy: 'cache-first',
useAlternateAfterCursor: true,
},
})
// We store the refetchAll callback in a ref since it will update when
// variables or result length change and we need to call an up-to-date
// version in the useEffect below to refetch the proper results.
//
// TODO: See if we can make refetchAll stable
const refetchAllRef = useRef(refetchAll)
useEffect(() => {
refetchAllRef.current = refetchAll
}, [refetchAll])
useEffect(() => {
if (showOlderCommits && refetchAllRef.current) {
// Updating the variables alone is not enough to force a loading
// indicator to show, so we need to refetch the results.
refetchAllRef.current()
}
}, [showOlderCommits])
const fileDecorationsByPath =
useObservable<FileDecorationsByPath>(
useMemo(
@ -159,26 +54,8 @@ export const TreePageContent: React.FunctionComponent<React.PropsWithChildren<Tr
)
) ?? {}
const onShowOlderCommitsClicked = useCallback((event: React.MouseEvent): void => {
event.preventDefault()
setShowOlderCommits(true)
}, [])
const showAllCommits = (
<Button
className="test-tree-page-show-all-commits"
onClick={onShowOlderCommitsClicked}
variant="secondary"
size="sm"
>
Show commits older than one year
</Button>
)
const { extensionsController } = props
const showLinkToCommitsPage = connection && hasNextPage && connection.nodes.length > TREE_COMMITS_PER_PAGE
return (
<>
<section className={classNames('test-tree-entries mb-3', styles.section)}>
@ -220,49 +97,7 @@ export const TreePageContent: React.FunctionComponent<React.PropsWithChildren<Tr
</ActionsContainer>
) : null}
<ConnectionContainer className={styles.section}>
<Heading as="h3" styleAs="h2">
Changes
</Heading>
{error && <ErrorAlert error={error} className="w-100 mb-0" />}
<ConnectionList className="list-group list-group-flush w-100">
{connection?.nodes.map(node => (
<GitCommitNode
key={node.id}
className={classNames('list-group-item', styles.gitCommitNode)}
messageSubjectClassName={styles.gitCommitNodeMessageSubject}
compact={true}
wrapperElement="li"
node={node}
/>
))}
</ConnectionList>
{loading && <ConnectionLoading />}
{connection && (
<SummaryContainer centered={true}>
<ConnectionSummary
centered={true}
first={TREE_COMMITS_PER_PAGE}
connection={connection}
noun={showOlderCommits ? 'commit' : 'commit in the past year'}
pluralNoun={showOlderCommits ? 'commits' : 'commits in the past year'}
hasNextPage={hasNextPage}
emptyElement={null}
/>
{hasNextPage ? (
showLinkToCommitsPage ? (
<Link to={`${repo.url}/-/commits${filePath ? `/${filePath}` : ''}`}>
Show all commits
</Link>
) : (
<ShowMoreButton centered={true} onClick={fetchMore} />
)
) : null}
{!hasNextPage && !showOlderCommits ? showAllCommits : null}
</SummaryContainer>
)}
</ConnectionContainer>
<TreeCommits repo={repo} commitID={commitID} filePath={filePath} className={styles.section} />
</>
)
}

View File

@ -0,0 +1,12 @@
.git-commit-node {
padding-left: 0;
padding-right: 0;
&-message-subject {
opacity: 0.9;
}
:global(.btn) {
opacity: 0.85;
}
}

View File

@ -0,0 +1,187 @@
import React, { useCallback, useState, useEffect, useMemo, useRef } from 'react'
import classNames from 'classnames'
import formatISO from 'date-fns/formatISO'
import subYears from 'date-fns/subYears'
import { dataOrThrowErrors, gql } from '@sourcegraph/http-client'
import { ErrorAlert, Button, Heading, Link } from '@sourcegraph/wildcard'
import { useShowMorePagination } from '../../../components/FilteredConnection/hooks/useShowMorePagination'
import {
ConnectionContainer,
SummaryContainer,
ConnectionList,
ConnectionLoading,
ShowMoreButton,
ConnectionSummary,
} from '../../../components/FilteredConnection/ui'
import {
GitCommitFields,
TreeCommitsResult,
TreeCommitsVariables,
TreePageRepositoryFields,
} from '../../../graphql-operations'
import { GitCommitNode } from '../../commits/GitCommitNode'
import { gitCommitFragment } from '../../commits/RepositoryCommitsPage'
import styles from './TreeCommits.module.scss'
interface Props {
repo: TreePageRepositoryFields
commitID: string
filePath: string
className?: string
}
const DEFAULT_FIRST = 10
/**
* A list of commits in a tree (or in the entire repository for the root tree).
*/
export const TreeCommits: React.FunctionComponent<Props> = ({ repo, commitID, filePath, className }) => {
const [showOlderCommits, setShowOlderCommits] = useState(false)
const after = useMemo(() => (showOlderCommits ? null : formatISO(subYears(Date.now(), 1))), [showOlderCommits])
const { connection, error, loading, hasNextPage, fetchMore, refetchAll } = useShowMorePagination<
TreeCommitsResult,
TreeCommitsVariables,
GitCommitFields
>({
query: gql`
query TreeCommits(
$repo: ID!
$revspec: String!
$first: Int
$filePath: String
$after: String
$afterCursor: String
) {
node(id: $repo) {
__typename
... on Repository {
commit(rev: $revspec) {
ancestors(first: $first, path: $filePath, after: $after, afterCursor: $afterCursor) {
nodes {
...GitCommitFields
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
}
${gitCommitFragment}
`,
variables: {
repo: repo.id,
revspec: commitID,
first: DEFAULT_FIRST,
filePath,
after,
afterCursor: null,
},
getConnection: result => {
const { node } = dataOrThrowErrors(result)
if (!node) {
return { nodes: [] }
}
if (node.__typename !== 'Repository') {
return { nodes: [] }
}
if (!node.commit?.ancestors) {
return { nodes: [] }
}
return node.commit.ancestors
},
options: {
fetchPolicy: 'cache-first',
useAlternateAfterCursor: true,
},
})
// We store the refetchAll callback in a ref since it will update when
// variables or result length change and we need to call an up-to-date
// version in the useEffect below to refetch the proper results.
//
// TODO: See if we can make refetchAll stable
const refetchAllRef = useRef(refetchAll)
useEffect(() => {
refetchAllRef.current = refetchAll
}, [refetchAll])
useEffect(() => {
if (showOlderCommits && refetchAllRef.current) {
// Updating the variables alone is not enough to force a loading
// indicator to show, so we need to refetch the results.
refetchAllRef.current()
}
}, [showOlderCommits])
const onShowOlderCommitsClicked = useCallback((event: React.MouseEvent): void => {
event.preventDefault()
setShowOlderCommits(true)
}, [])
const showAllCommits = (
<Button
className="test-tree-page-show-all-commits"
onClick={onShowOlderCommitsClicked}
variant="secondary"
size="sm"
>
Show commits older than one year
</Button>
)
const showLinkToCommitsPage = connection && hasNextPage && connection.nodes.length > DEFAULT_FIRST
return (
<ConnectionContainer className={className}>
<Heading as="h3" styleAs="h2">
Changes
</Heading>
{error && <ErrorAlert error={error} className="w-100 mb-0" />}
<ConnectionList className="list-group list-group-flush w-100">
{connection?.nodes.map(node => (
<GitCommitNode
key={node.id}
className={classNames('list-group-item', styles.gitCommitNode)}
messageSubjectClassName={styles.gitCommitNodeMessageSubject}
compact={true}
wrapperElement="li"
node={node}
/>
))}
</ConnectionList>
{loading && <ConnectionLoading />}
{connection && (
<SummaryContainer centered={true}>
<ConnectionSummary
centered={true}
first={DEFAULT_FIRST}
connection={connection}
noun={showOlderCommits ? 'commit' : 'commit in the past year'}
pluralNoun={showOlderCommits ? 'commits' : 'commits in the past year'}
hasNextPage={hasNextPage}
emptyElement={null}
/>
{hasNextPage ? (
showLinkToCommitsPage ? (
<Link to={`${repo.url}/-/commits${filePath ? `/${filePath}` : ''}`}>Show all commits</Link>
) : (
<ShowMoreButton centered={true} onClick={fetchMore} />
)
) : null}
{!hasNextPage && !showOlderCommits ? showAllCommits : null}
</SummaryContainer>
)}
</ConnectionContainer>
)
}