add support for submodules and symlinks (#378)

This commit is contained in:
Calvin Lee 2023-08-31 17:38:53 +00:00 committed by Harness
parent ac8f1932ba
commit c131873c82
7 changed files with 95 additions and 34 deletions

View File

@ -1,5 +1,4 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.3333 6.30333H13.3333V4.66699H7.99996L6.66663 2.66699H2.66663V13.3337M15.3333 6.30333L13.3333 13.3337H2.66663M15.3333 6.30333H4.66663L2.66663 13.3337" stroke="#383946" stroke-linecap="round" stroke-linejoin="round"/> <path d="M1.16406 6.41683V2.6835C1.16406 2.59067 1.20094 2.50165 1.26658 2.43601C1.33221 2.37037 1.42124 2.3335 1.51406 2.3335H5.1179C5.20132 2.33349 5.282 2.36328 5.3454 2.4175L7.19106 3.9995C7.25446 4.05371 7.33514 4.0835 7.41856 4.0835H12.4807C12.5267 4.0835 12.5722 4.09255 12.6147 4.11014C12.6571 4.12773 12.6957 4.15351 12.7282 4.18601C12.7607 4.21851 12.7865 4.25709 12.8041 4.29956C12.8217 4.34202 12.8307 4.38753 12.8307 4.4335V6.41683M1.16406 6.41683V11.3168C1.16406 11.4097 1.20094 11.4987 1.26658 11.5643C1.33221 11.63 1.42124 11.6668 1.51406 11.6668H12.4807C12.5736 11.6668 12.6626 11.63 12.7282 11.5643C12.7939 11.4987 12.8307 11.4097 12.8307 11.3168V6.41683M1.16406 6.41683H6.9974H12.8307" stroke="#4F5162" stroke-width="0.875" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15 6.5H4.5L3 13H13L15 6.5Z" fill="#383946"/> <rect x="1" y="6" width="12" height="6" fill="#4F5162"/>
<path opacity="0.3" d="M8.66663 2.66634H13.3333" stroke="#383946" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 479 B

After

Width:  |  Height:  |  Size: 929 B

View File

@ -0,0 +1,5 @@
<svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.16406 6.41683V2.6835C1.16406 2.59067 1.20094 2.50165 1.26658 2.43601C1.33221 2.37037 1.42124 2.3335 1.51406 2.3335H5.1179C5.20132 2.33349 5.282 2.36328 5.3454 2.4175L7.19106 3.9995C7.25446 4.05371 7.33514 4.0835 7.41856 4.0835H12.4807C12.5267 4.0835 12.5722 4.09255 12.6147 4.11014C12.6571 4.12773 12.6957 4.15351 12.7282 4.18601C12.7607 4.21851 12.7865 4.25709 12.8041 4.29956C12.8217 4.34202 12.8307 4.38753 12.8307 4.4335V6.41683M1.16406 6.41683V11.3168C1.16406 11.4097 1.20094 11.4987 1.26658 11.5643C1.33221 11.63 1.42124 11.6668 1.51406 11.6668H12.4807C12.5736 11.6668 12.6626 11.63 12.7282 11.5643C12.7939 11.4987 12.8307 11.4097 12.8307 11.3168V6.41683M1.16406 6.41683H6.9974H12.8307" stroke="#4F5162" stroke-width="0.875" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="1" y="6" width="12" height="6" fill="#4F5162"/>
<path d="M7 7.6665L8.33333 8.99984M8.33333 8.99984H5M8.33333 8.99984L7 10.3332" stroke="white" stroke-width="0.88" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,4 @@
<svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 7.6665L8.33333 8.99984M8.33333 8.99984H5M8.33333 8.99984L7 10.3332M2.33594 12.4832V1.5165C2.33594 1.42368 2.37281 1.33465 2.43845 1.26902C2.50409 1.20338 2.59311 1.1665 2.68594 1.1665H9.48294C9.57573 1.16659 9.6647 1.20351 9.73027 1.26917L11.5666 3.1055C11.5993 3.13811 11.6251 3.17685 11.6428 3.2195C11.6604 3.26215 11.6694 3.30786 11.6693 3.354V12.4832C11.6693 12.5291 11.6602 12.5746 11.6426 12.6171C11.625 12.6596 11.5993 12.6982 11.5668 12.7307C11.5343 12.7632 11.4957 12.7889 11.4532 12.8065C11.4107 12.8241 11.3652 12.8332 11.3193 12.8332H2.68594C2.63997 12.8332 2.59446 12.8241 2.552 12.8065C2.50953 12.7889 2.47095 12.7632 2.43845 12.7307C2.40595 12.6982 2.38017 12.6596 2.36258 12.6171C2.34499 12.5746 2.33594 12.5291 2.33594 12.4832Z" stroke="#4F5162" stroke-width="0.875" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.33594 1.1665V3.14984C9.33594 3.24266 9.37281 3.33169 9.43845 3.39732C9.50409 3.46296 9.59311 3.49984 9.68594 3.49984H11.6693" stroke="#4F5162" stroke-width="0.875" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -35,7 +35,7 @@ import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButto
import { PlainButton } from 'components/PlainButton/PlainButton' import { PlainButton } from 'components/PlainButton/PlainButton'
import { CommitsView } from 'components/CommitsView/CommitsView' import { CommitsView } from 'components/CommitsView/CommitsView'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam' import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import { FileCategory, useFileContentViewerDecision } from 'utils/FileUtils' import { FileCategory, RepoContentExtended, useFileContentViewerDecision } from 'utils/FileUtils'
import { useDownloadRawFile } from 'hooks/useDownloadRawFile' import { useDownloadRawFile } from 'hooks/useDownloadRawFile'
import { usePageIndex } from 'hooks/usePageIndex' import { usePageIndex } from 'hooks/usePageIndex'
import { Readme } from '../FolderContent/Readme' import { Readme } from '../FolderContent/Readme'
@ -233,7 +233,12 @@ export function FileContent({
</Layout.Horizontal> </Layout.Horizontal>
</Layout.Horizontal> </Layout.Horizontal>
<Render when={(resourceContent?.content as RepoFileContent)?.data}> <Render
when={
(resourceContent?.content as RepoFileContent)?.data ||
(resourceContent?.content as RepoContentExtended)?.target ||
(resourceContent?.content as RepoContentExtended)?.url
}>
<Container className={css.content}> <Container className={css.content}>
<Match expr={isViewable}> <Match expr={isViewable}>
<Falsy> <Falsy>
@ -375,6 +380,18 @@ export function FileContent({
source={decodeGitContent(base64Data)} source={decodeGitContent(base64Data)}
/> />
</Case> </Case>
<Case val={FileCategory.SUBMODULE}>
<SourceCodeViewer
language={filenameToLanguage(filename)}
source={decodeGitContent(base64Data)}
/>
</Case>
<Case val={FileCategory.SYMLINK}>
<SourceCodeViewer
language={filenameToLanguage(filename)}
source={decodeGitContent(base64Data)}
/>
</Case>
</Match> </Match>
</Center> </Center>
</Falsy> </Falsy>

View File

@ -9,7 +9,7 @@
text-transform: none; text-transform: none;
color: var(--grey-400); color: var(--grey-400);
font-weight: 500; font-weight: 500;
font-size: 13px; font-size: 12px;
} }
} }
@ -24,7 +24,7 @@
margin-bottom: 0; margin-bottom: 0;
.rowText { .rowText {
font-size: 13px; font-size: 12px;
font-weight: 400; font-weight: 400;
} }
} }
@ -63,7 +63,7 @@
&, &,
.text { .text {
font-size: 13px; font-size: 12px;
font-weight: 400; font-weight: 400;
cursor: pointer !important; cursor: pointer !important;
} }

View File

@ -9,10 +9,11 @@ import {
TextProps, TextProps,
useIsMounted useIsMounted
} from '@harnessio/uicore' } from '@harnessio/uicore'
import { Icon } from '@harnessio/icons'
import { Color } from '@harnessio/design-system' import { Color } from '@harnessio/design-system'
import cx from 'classnames' import cx from 'classnames'
import type { CellProps, Column } from 'react-table' import type { CellProps, Column } from 'react-table'
import { Page } from 'iconoir-react'
import { Render } from 'react-jsx-match' import { Render } from 'react-jsx-match'
import { chunk, sortBy, throttle } from 'lodash-es' import { chunk, sortBy, throttle } from 'lodash-es'
import { useMutate } from 'restful-react' import { useMutate } from 'restful-react'
@ -20,17 +21,30 @@ import { Link, useHistory } from 'react-router-dom'
import { useAppContext } from 'AppContext' import { useAppContext } from 'AppContext'
import type { OpenapiContentInfo, OpenapiDirContent, TypesCommit } from 'services/code' import type { OpenapiContentInfo, OpenapiDirContent, TypesCommit } from 'services/code'
import { formatDate, isInViewport, LIST_FETCHING_LIMIT } from 'utils/Utils' import { formatDate, isInViewport, LIST_FETCHING_LIMIT } from 'utils/Utils'
import { findReadmeInfo, CodeIcon, GitInfoProps, isFile } from 'utils/GitUtils' import { findReadmeInfo, GitInfoProps, isFile, isSymlink, isSubmodule } from 'utils/GitUtils'
import { LatestCommitForFolder } from 'components/LatestCommit/LatestCommit' import { LatestCommitForFolder } from 'components/LatestCommit/LatestCommit'
import { CommitActions } from 'components/CommitActions/CommitActions' import { CommitActions } from 'components/CommitActions/CommitActions'
import { useEventListener } from 'hooks/useEventListener' import { useEventListener } from 'hooks/useEventListener'
import { Readme } from './Readme' import { Readme } from './Readme'
import repositoryCSS from '../../Repository.module.scss' import repositoryCSS from '../../Repository.module.scss'
import CodeFile from '../../../../icons/CodeFileFill.svg' import CodeFolder from '../../../../icons/CodeFileFill.svg'
import Submodule from '../../../../icons/Submodules.svg'
import Symlink from '../../../../icons/Symlink.svg'
import css from './FolderContent.module.scss' import css from './FolderContent.module.scss'
type FolderContentProps = Pick<GitInfoProps, 'repoMetadata' | 'resourceContent' | 'gitRef'> type FolderContentProps = Pick<GitInfoProps, 'repoMetadata' | 'resourceContent' | 'gitRef'>
const checkIcon = (row: OpenapiContentInfo): React.ReactElement => {
if (isFile(row)) {
return <Page width={14} height={14} />
} else if (isSymlink(row)) {
return <img width={14} height={14} src={Symlink} />
} else if (isSubmodule(row)) {
return <img width={14} height={14} src={Submodule} />
} else {
return <img width={14} height={14} src={CodeFolder} />
}
}
export function FolderContent({ repoMetadata, resourceContent, gitRef }: FolderContentProps) { export function FolderContent({ repoMetadata, resourceContent, gitRef }: FolderContentProps) {
const history = useHistory() const history = useHistory()
const { routes, standalone } = useAppContext() const { routes, standalone } = useAppContext()
@ -40,23 +54,25 @@ export function FolderContent({ repoMetadata, resourceContent, gitRef }: FolderC
Header: 'Files', Header: 'Files',
id: 'name', id: 'name',
width: '30%', width: '30%',
Cell: ({ row }: CellProps<OpenapiContentInfo>) => ( Cell: ({ row }: CellProps<OpenapiContentInfo>) => {
<Container> return (
<Layout.Horizontal spacing="small"> <Container>
{isFile(row.original) ? <Icon name={CodeIcon.File} /> : <img width={16} height={16} src={CodeFile} />} <Layout.Horizontal spacing="small">
<ListingItemLink {checkIcon(row.original)}
url={routes.toCODERepository({ <ListingItemLink
repoPath: repoMetadata.path as string, url={routes.toCODERepository({
gitRef, repoPath: repoMetadata.path as string,
resourcePath: row.original.path gitRef,
})} resourcePath: row.original.path
text={row.original.name as string} })}
data-resource-path={row.original.path} text={row.original.name as string}
lineClamp={1} data-resource-path={row.original.path}
/> lineClamp={1}
</Layout.Horizontal> />
</Container> </Layout.Horizontal>
) </Container>
)
}
}, },
{ {
Header: 'Date', Header: 'Date',

View File

@ -22,6 +22,13 @@ interface UseFileViewerDecisionResult {
isText: boolean isText: boolean
} }
export interface RepoContentExtended extends RepoFileContent {
size?: number
target?: string
commit_sha?: string
url?: string
}
export function useFileContentViewerDecision({ export function useFileContentViewerDecision({
repoMetadata, repoMetadata,
gitRef, gitRef,
@ -32,6 +39,9 @@ export function useFileContentViewerDecision({
const metadata = useMemo(() => { const metadata = useMemo(() => {
const filename = resourceContent.name as string const filename = resourceContent.name as string
const extension = filename?.split('.').pop() || '' const extension = filename?.split('.').pop() || ''
const isSymlink = resourceContent?.type === 'symlink'
const isSubmodule = resourceContent?.type === 'submodule'
const isMarkdown = extension.toLowerCase() === 'md' const isMarkdown = extension.toLowerCase() === 'md'
const isPdf = extension.toLowerCase() === 'pdf' const isPdf = extension.toLowerCase() === 'pdf'
const isSVG = extension.toLowerCase() === 'svg' const isSVG = extension.toLowerCase() === 'svg'
@ -40,7 +50,9 @@ export function useFileContentViewerDecision({
const isVideo = VideoExtensions.includes(extension.toLowerCase()) const isVideo = VideoExtensions.includes(extension.toLowerCase())
const isText = !!( const isText = !!(
SpecialTextFiles.find(name => name.toLowerCase() === filename?.toLowerCase()) || SpecialTextFiles.find(name => name.toLowerCase() === filename?.toLowerCase()) ||
TextExtensions.includes(extension.toLowerCase()) TextExtensions.includes(extension.toLowerCase()) ||
isSymlink ||
isSubmodule
) )
const category = isMarkdown const category = isMarkdown
? FileCategory.MARKDOWN ? FileCategory.MARKDOWN
@ -54,12 +66,17 @@ export function useFileContentViewerDecision({
? FileCategory.AUDIO ? FileCategory.AUDIO
: isVideo : isVideo
? FileCategory.VIDEO ? FileCategory.VIDEO
: isSymlink
? FileCategory.SYMLINK
: isSubmodule
? FileCategory.SUBMODULE
: isText : isText
? FileCategory.TEXT ? FileCategory.TEXT
: FileCategory.OTHER : FileCategory.OTHER
const isViewable = isPdf || isSVG || isImage || isAudio || isVideo || isText const isViewable = isPdf || isSVG || isImage || isAudio || isVideo || isText || isSubmodule || isSymlink
const resourceData = resourceContent?.content as RepoFileContent const resourceData = resourceContent?.content as RepoContentExtended
const isFileTooLarge = resourceData?.size !== resourceData?.data_size const isFileTooLarge =
resourceData?.size && resourceData?.data_size ? resourceData?.size !== resourceData?.data_size : false
const rawURL = `/code/api/v1/repos/${repoMetadata?.path}/+/raw/${resourcePath}?routingId=${routingId}&git_ref=${gitRef}` const rawURL = `/code/api/v1/repos/${repoMetadata?.path}/+/raw/${resourcePath}?routingId=${routingId}&git_ref=${gitRef}`
return { return {
category, category,
@ -73,7 +90,8 @@ export function useFileContentViewerDecision({
size: resourceData?.size || 0, size: resourceData?.size || 0,
// base64 data returned from content API. This snapshot can be truncated by backend // base64 data returned from content API. This snapshot can be truncated by backend
base64Data: resourceData?.data || '', base64Data: resourceData?.data || resourceData?.target || resourceData?.url || '',
rawURL rawURL
} }
}, [resourceContent.content]) // eslint-disable-line react-hooks/exhaustive-deps }, [resourceContent.content]) // eslint-disable-line react-hooks/exhaustive-deps
@ -91,6 +109,8 @@ export enum FileCategory {
AUDIO = 'AUDIO', AUDIO = 'AUDIO',
VIDEO = 'VIDEO', VIDEO = 'VIDEO',
TEXT = 'TEXT', TEXT = 'TEXT',
SYMLINK = 'SYMLINK',
SUBMODULE = 'SUBMODULE',
OTHER = 'OTHER' OTHER = 'OTHER'
} }