mirror of
https://github.com/harness/drone.git
synced 2025-05-01 13:11:27 +00:00
feat: [code-120]: internal page refresh button (#316)
This commit is contained in:
parent
f2358bd4ec
commit
e179696678
@ -40,3 +40,16 @@
|
||||
.container {
|
||||
background-color: var(--primary-bg) !important;
|
||||
}
|
||||
|
||||
.hideBtn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.refreshIcon {
|
||||
padding-left: var(--spacing-xsmall) !important;
|
||||
padding-right: var(--spacing-xsmall);
|
||||
}
|
||||
|
||||
.repeatBtn {
|
||||
margin-left: var(--spacing-xsmall) !important;
|
||||
}
|
||||
|
@ -7,5 +7,8 @@ declare const styles: {
|
||||
readonly main: string
|
||||
readonly enableDiffLineBreaks: string
|
||||
readonly container: string
|
||||
readonly hideBtn: string
|
||||
readonly refreshIcon: string
|
||||
readonly repeatBtn: string
|
||||
}
|
||||
export default styles
|
||||
|
@ -7,7 +7,8 @@ import {
|
||||
Text,
|
||||
StringSubstitute,
|
||||
Button,
|
||||
PageError
|
||||
PageError,
|
||||
ButtonSize
|
||||
} from '@harness/uicore'
|
||||
import { Match, Case, Render } from 'react-jsx-match'
|
||||
import * as Diff2Html from 'diff2html'
|
||||
@ -50,6 +51,8 @@ interface ChangesProps extends Pick<GitInfoProps, 'repoMetadata'> {
|
||||
pullRequestMetadata?: TypesPullReq
|
||||
className?: string
|
||||
onCommentUpdate: () => void
|
||||
prHasChanged?: boolean
|
||||
handleRefresh?: () => void
|
||||
}
|
||||
|
||||
export const Changes: React.FC<ChangesProps> = ({
|
||||
@ -61,7 +64,9 @@ export const Changes: React.FC<ChangesProps> = ({
|
||||
emptyMessage,
|
||||
pullRequestMetadata,
|
||||
onCommentUpdate,
|
||||
className
|
||||
className,
|
||||
prHasChanged,
|
||||
handleRefresh
|
||||
}) => {
|
||||
const { getString } = useStrings()
|
||||
const [viewStyle, setViewStyle] = useUserPreference(UserPreference.DIFF_VIEW_STYLE, ViewStyle.SIDE_BY_SIDE)
|
||||
@ -174,6 +179,18 @@ export const Changes: React.FC<ChangesProps> = ({
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
{!prHasChanged ? null : (
|
||||
<Button
|
||||
onClick={handleRefresh}
|
||||
iconProps={{ className: css.refreshIcon, size: 12 }}
|
||||
icon="repeat"
|
||||
text={getString('refresh')}
|
||||
variation={ButtonVariation.SECONDARY}
|
||||
size={ButtonSize.SMALL}
|
||||
padding={{ left: 'small' }}
|
||||
className={css.repeatBtn}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Show "Scroll to top" button */}
|
||||
<Render when={isSticky}>
|
||||
|
@ -39,3 +39,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.refreshIcon {
|
||||
padding-left: var(--spacing-xsmall) !important;
|
||||
padding-right: var(--spacing-xsmall);
|
||||
}
|
@ -6,5 +6,6 @@ declare const styles: {
|
||||
readonly row: string
|
||||
readonly rowText: string
|
||||
readonly label: string
|
||||
readonly refreshIcon: string
|
||||
}
|
||||
export default styles
|
||||
|
@ -1,5 +1,16 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import { Container, Color, TableV2 as Table, Text, Avatar, Layout } from '@harness/uicore'
|
||||
import {
|
||||
Container,
|
||||
Color,
|
||||
TableV2 as Table,
|
||||
Text,
|
||||
Avatar,
|
||||
Layout,
|
||||
ButtonVariation,
|
||||
ButtonSize,
|
||||
Button,
|
||||
FlexExpander
|
||||
} from '@harness/uicore'
|
||||
import type { CellProps, Column } from 'react-table'
|
||||
import { orderBy } from 'lodash-es'
|
||||
import { useStrings } from 'framework/strings'
|
||||
@ -16,9 +27,18 @@ interface CommitsViewProps extends Pick<GitInfoProps, 'repoMetadata'> {
|
||||
commits: TypesCommit[]
|
||||
emptyTitle: string
|
||||
emptyMessage: string
|
||||
prHasChanged: boolean
|
||||
handleRefresh: () => void
|
||||
}
|
||||
|
||||
export function CommitsView({ repoMetadata, commits, emptyTitle, emptyMessage }: CommitsViewProps) {
|
||||
export function CommitsView({
|
||||
repoMetadata,
|
||||
commits,
|
||||
emptyTitle,
|
||||
emptyMessage,
|
||||
handleRefresh,
|
||||
prHasChanged
|
||||
}: CommitsViewProps) {
|
||||
const { getString } = useStrings()
|
||||
const { routes } = useAppContext()
|
||||
const columns: Column<TypesCommit>[] = useMemo(
|
||||
@ -79,6 +99,20 @@ export function CommitsView({ repoMetadata, commits, emptyTitle, emptyMessage }:
|
||||
|
||||
return (
|
||||
<Container className={css.container}>
|
||||
<Layout.Horizontal>
|
||||
<FlexExpander />
|
||||
{!prHasChanged ? null : (
|
||||
<Button
|
||||
onClick={handleRefresh}
|
||||
iconProps={{ className: css.refreshIcon, size: 12 }}
|
||||
icon="repeat"
|
||||
text={getString('refresh')}
|
||||
variation={ButtonVariation.SECONDARY}
|
||||
size={ButtonSize.SMALL}
|
||||
margin={{ bottom: 'small' }}
|
||||
/>
|
||||
)}
|
||||
</Layout.Horizontal>
|
||||
{!!commits.length &&
|
||||
Object.entries(commitsGroupedByDate).map(([date, commitsByDate]) => {
|
||||
return (
|
||||
|
@ -220,6 +220,7 @@ export interface StringsMap {
|
||||
pullRequests: string
|
||||
quote: string
|
||||
readMe: string
|
||||
refresh: string
|
||||
rejected: string
|
||||
remove: string
|
||||
renameFile: string
|
||||
|
@ -329,4 +329,5 @@ required: Required
|
||||
noneYet: None Yet
|
||||
noOptionalReviewers: No Optional Reviewers
|
||||
noRequiredReviewers: No Required Reviewers
|
||||
reviewers: Reviewers
|
||||
reviewers: Reviewers
|
||||
refresh: Refresh
|
||||
|
@ -82,3 +82,32 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.refreshIcon {
|
||||
padding-left: var(--spacing-xsmall) !important;
|
||||
padding-right: var(--spacing-xsmall);
|
||||
}
|
||||
|
||||
.repeatBtn:hover {
|
||||
box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.04), 0px 2px 4px rgba(96, 97, 112, 0.16) !important;
|
||||
}
|
||||
.repeatBtn {
|
||||
margin-left: var(--spacing-xsmall) !important;
|
||||
padding: 5px 1px !important;
|
||||
background-color: var(--orange-100) !important;
|
||||
color: #c05809 !important;
|
||||
--background-color-hover: var(--grey-50) !important;
|
||||
--background-color-active: var(--grey-100) !important;
|
||||
--border: none !important;
|
||||
--padding: 12px;
|
||||
--icon-padding: 4px;
|
||||
--font-size: 13px;
|
||||
--font-weight: 500;
|
||||
min-height: 24px !important;
|
||||
height: 24px !important;
|
||||
|
||||
.bp3-button-text {
|
||||
color: var(--orange-300);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,5 +9,8 @@ declare const styles: {
|
||||
readonly snapshotContent: string
|
||||
readonly newCommentCreated: string
|
||||
readonly clear: string
|
||||
readonly refreshIcon: string
|
||||
readonly repeatBtn: string
|
||||
readonly bp3ButtonText: string
|
||||
}
|
||||
export default styles
|
||||
|
@ -1,6 +1,9 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
ButtonSize,
|
||||
ButtonVariation,
|
||||
Color,
|
||||
Container,
|
||||
FlexExpander,
|
||||
@ -38,9 +41,17 @@ import css from './Conversation.module.scss'
|
||||
|
||||
interface ConversationProps extends Pick<GitInfoProps, 'repoMetadata' | 'pullRequestMetadata'> {
|
||||
onCommentUpdate: () => void
|
||||
prHasChanged?: boolean
|
||||
handleRefresh?: () => void
|
||||
}
|
||||
|
||||
export const Conversation: React.FC<ConversationProps> = ({ repoMetadata, pullRequestMetadata, onCommentUpdate }) => {
|
||||
export const Conversation: React.FC<ConversationProps> = ({
|
||||
repoMetadata,
|
||||
pullRequestMetadata,
|
||||
onCommentUpdate,
|
||||
prHasChanged,
|
||||
handleRefresh
|
||||
}) => {
|
||||
const { getString } = useStrings()
|
||||
const { currentUser } = useAppContext()
|
||||
const {
|
||||
@ -126,6 +137,21 @@ export const Conversation: React.FC<ConversationProps> = ({ repoMetadata, pullRe
|
||||
onPRStateChanged={refreshPR}
|
||||
/>
|
||||
<Container>
|
||||
<Layout.Horizontal width={`70%`}>
|
||||
<FlexExpander />
|
||||
{!prHasChanged ? null : (
|
||||
<Button
|
||||
onClick={handleRefresh}
|
||||
iconProps={{ className: css.refreshIcon, size: 12 }}
|
||||
icon="repeat"
|
||||
text={getString('refresh')}
|
||||
variation={ButtonVariation.SECONDARY}
|
||||
size={ButtonSize.SMALL}
|
||||
margin={{ bottom: 'small' }}
|
||||
/>
|
||||
)}
|
||||
</Layout.Horizontal>
|
||||
|
||||
<Layout.Horizontal>
|
||||
<Container width={`70%`}>
|
||||
<Layout.Vertical spacing="xlarge">
|
||||
|
@ -74,9 +74,9 @@ const PullRequestSideBar = (props: PullRequestSideBarProps) => {
|
||||
{getString('required')}
|
||||
</Text>
|
||||
{reviewers && reviewers?.length !== 0 ? (
|
||||
reviewers.map((reviewer: { reviewer: { display_name: string }; review_decision: string }): any => {
|
||||
reviewers.map((reviewer: { reviewer: { display_name: string, id:number }; review_decision: string }): any => {
|
||||
return (
|
||||
<Layout.Horizontal>
|
||||
<Layout.Horizontal key={reviewer.reviewer.id}>
|
||||
<Icon className={css.reviewerPadding} {...generateReviewDecisionIcon(reviewer.review_decision)} />
|
||||
<Avatar
|
||||
className={css.reviewerAvatar}
|
||||
@ -129,9 +129,9 @@ const PullRequestSideBar = (props: PullRequestSideBarProps) => {
|
||||
{getString('optional')}
|
||||
</Text>
|
||||
{reviewers && reviewers?.length !== 0 ? (
|
||||
reviewers.map((reviewer: { reviewer: { display_name: string }; review_decision: string }) => {
|
||||
reviewers.map((reviewer: { reviewer: { display_name: string, id: number }; review_decision: string }) => {
|
||||
return (
|
||||
<Layout.Horizontal>
|
||||
<Layout.Horizontal key={reviewer.reviewer.id}>
|
||||
<Icon className={css.reviewerPadding} name="dot" />
|
||||
<Avatar
|
||||
className={css.reviewerAvatar}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import {
|
||||
Container,
|
||||
PageBody,
|
||||
@ -21,7 +21,7 @@ import { useAppContext } from 'AppContext'
|
||||
import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { RepositoryPageHeader } from 'components/RepositoryPageHeader/RepositoryPageHeader'
|
||||
import { voidFn, getErrorMessage } from 'utils/Utils'
|
||||
import { voidFn, getErrorMessage, PR_POLLING_LIMIT } from 'utils/Utils'
|
||||
import { CodeIcon, GitInfoProps } from 'utils/GitUtils'
|
||||
import type { TypesPullReq, TypesRepository } from 'services/code'
|
||||
import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
|
||||
@ -41,6 +41,7 @@ export default function PullRequest() {
|
||||
const history = useHistory()
|
||||
const { getString } = useStrings()
|
||||
const { routes } = useAppContext()
|
||||
const [prHasChanged, setPrHasChanged] = useState(false)
|
||||
const {
|
||||
repoMetadata,
|
||||
error,
|
||||
@ -49,6 +50,7 @@ export default function PullRequest() {
|
||||
pullRequestId,
|
||||
pullRequestSection = PullRequestSection.CONVERSATION
|
||||
} = useGetRepositoryMetadata()
|
||||
|
||||
const {
|
||||
data: prData,
|
||||
error: prError,
|
||||
@ -58,6 +60,40 @@ export default function PullRequest() {
|
||||
path: `/api/v1/repos/${repoMetadata?.path}/+/pullreq/${pullRequestId}`,
|
||||
lazy: !repoMetadata
|
||||
})
|
||||
const {
|
||||
data: pollPrData,
|
||||
error: pollPrError,
|
||||
refetch: refetchPollPullRequest
|
||||
} = useGet<TypesPullReq>({
|
||||
path: `/api/v1/repos/${repoMetadata?.path}/+/pullreq/${pullRequestId}`,
|
||||
lazy: !repoMetadata
|
||||
})
|
||||
const [newPrData, setNewPrData] = useState<TypesPullReq>()
|
||||
|
||||
const handleRefresh = () => {
|
||||
refetchPullRequest()
|
||||
setNewPrData(prData as TypesPullReq)
|
||||
setPrHasChanged(false)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const interval = window.setTimeout(() => {
|
||||
refetchPollPullRequest()
|
||||
setNewPrData(pollPrData as TypesPullReq)
|
||||
}, PR_POLLING_LIMIT)
|
||||
return () => window.clearTimeout(interval)
|
||||
}, [pollPrData])
|
||||
|
||||
useEffect(() => {
|
||||
if (prData?.stats && newPrData?.stats) {
|
||||
const prStatsChanged =
|
||||
prData.stats.commits !== newPrData.stats.commits || prData.stats.files_changed !== newPrData.stats.files_changed
|
||||
if (prStatsChanged) {
|
||||
setPrHasChanged(prStatsChanged)
|
||||
}
|
||||
}
|
||||
}, [newPrData])
|
||||
|
||||
const activeTab = useMemo(
|
||||
() =>
|
||||
Object.values(PullRequestSection).find(value => value === pullRequestSection)
|
||||
@ -65,6 +101,7 @@ export default function PullRequest() {
|
||||
: PullRequestSection.CONVERSATION,
|
||||
[pullRequestSection]
|
||||
)
|
||||
// /repos/${paramsInPath.repo_ref}/pullreq/${paramsInPath.pullreq_number}/metadata
|
||||
|
||||
return (
|
||||
<Container className={css.main}>
|
||||
@ -81,7 +118,7 @@ export default function PullRequest() {
|
||||
]
|
||||
}
|
||||
/>
|
||||
<PageBody error={getErrorMessage(error || prError)} retryOnError={voidFn(refetch)}>
|
||||
<PageBody error={getErrorMessage(error || prError || pollPrError)} retryOnError={voidFn(refetch)}>
|
||||
<LoadingSpinner visible={loading || prLoading} withBorder={!!prData && prLoading} />
|
||||
|
||||
<Render when={repoMetadata && prData}>
|
||||
@ -116,6 +153,8 @@ export default function PullRequest() {
|
||||
repoMetadata={repoMetadata as TypesRepository}
|
||||
pullRequestMetadata={prData as TypesPullReq}
|
||||
onCommentUpdate={voidFn(refetchPullRequest)}
|
||||
prHasChanged={prHasChanged}
|
||||
handleRefresh={voidFn(handleRefresh)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
@ -132,6 +171,8 @@ export default function PullRequest() {
|
||||
<PullRequestCommits
|
||||
repoMetadata={repoMetadata as TypesRepository}
|
||||
pullRequestMetadata={prData as TypesPullReq}
|
||||
prHasChanged={prHasChanged}
|
||||
handleRefresh={voidFn(handleRefresh)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
@ -154,6 +195,8 @@ export default function PullRequest() {
|
||||
emptyTitle={getString('noChanges')}
|
||||
emptyMessage={getString('noChangesPR')}
|
||||
onCommentUpdate={voidFn(refetchPullRequest)}
|
||||
prHasChanged={prHasChanged}
|
||||
handleRefresh={voidFn(handleRefresh)}
|
||||
/>
|
||||
</Container>
|
||||
)
|
||||
|
@ -9,9 +9,16 @@ import { ResourceListingPagination } from 'components/ResourceListingPagination/
|
||||
import { CommitsView } from 'components/CommitsView/CommitsView'
|
||||
import { PullRequestTabContentWrapper } from '../PullRequestTabContentWrapper'
|
||||
|
||||
export const PullRequestCommits: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'pullRequestMetadata'>> = ({
|
||||
interface CommitProps extends Pick<GitInfoProps, 'repoMetadata' | 'pullRequestMetadata'> {
|
||||
prHasChanged: boolean
|
||||
handleRefresh: () => void
|
||||
}
|
||||
|
||||
export const PullRequestCommits: React.FC<CommitProps> = ({
|
||||
repoMetadata,
|
||||
pullRequestMetadata
|
||||
pullRequestMetadata,
|
||||
prHasChanged,
|
||||
handleRefresh
|
||||
}) => {
|
||||
const limit = LIST_FETCHING_LIMIT
|
||||
const [page, setPage] = usePageIndex()
|
||||
@ -40,6 +47,8 @@ export const PullRequestCommits: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'p
|
||||
repoMetadata={repoMetadata}
|
||||
emptyTitle={getString('noCommits')}
|
||||
emptyMessage={getString('noCommitsPR')}
|
||||
prHasChanged={prHasChanged}
|
||||
handleRefresh={voidFn(handleRefresh)}
|
||||
/>
|
||||
|
||||
<ResourceListingPagination response={response} page={page} setPage={setPage} />
|
||||
|
@ -10,6 +10,7 @@ export const DEFAULT_BRANCH_NAME = 'main'
|
||||
export const REGEX_VALID_REPO_NAME = /^[a-zA-Z_][0-9a-zA-Z-_.$]*$/
|
||||
export const SUGGESTED_BRANCH_NAMES = [DEFAULT_BRANCH_NAME, 'master']
|
||||
export const FILE_SEPERATOR = '/'
|
||||
export const PR_POLLING_LIMIT = 15000
|
||||
|
||||
/** This utility shows a toaster without being bound to any component.
|
||||
* It's useful to show cross-page/component messages */
|
||||
|
Loading…
x
Reference in New Issue
Block a user