mirror of
https://github.com/harness/drone.git
synced 2025-05-31 11:43:15 +00:00
Merge branch 'ui/code-comment-new-api' of _OKE5H2PQKOUfzFFDuD4FA/default/CODE/gitness (#46)
This commit is contained in:
commit
773c465c44
@ -23,16 +23,10 @@ import { useEventListener } from 'hooks/useEventListener'
|
||||
import { UserPreference, useUserPreference } from 'hooks/useUserPreference'
|
||||
import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator'
|
||||
import type { DiffFileEntry } from 'utils/types'
|
||||
import {
|
||||
DIFF2HTML_CONFIG,
|
||||
PR_CODE_COMMENT_PAYLOAD_VERSION,
|
||||
PullRequestCodeCommentPayload,
|
||||
ViewStyle
|
||||
} from 'components/DiffViewer/DiffViewerUtils'
|
||||
import { DIFF2HTML_CONFIG, ViewStyle } from 'components/DiffViewer/DiffViewerUtils'
|
||||
import { NoResultCard } from 'components/NoResultCard/NoResultCard'
|
||||
import type { TypesPullReq, TypesPullReqActivity } from 'services/code'
|
||||
import { useShowRequestError } from 'hooks/useShowRequestError'
|
||||
// import { Render } from 'components/Render/Render'
|
||||
import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
|
||||
import { ChangesDropdown } from './ChangesDropdown'
|
||||
import { DiffViewConfiguration } from './DiffViewConfiguration'
|
||||
@ -79,7 +73,8 @@ export const Changes: React.FC<ChangesProps> = ({
|
||||
data: rawDiff,
|
||||
error,
|
||||
loading,
|
||||
refetch
|
||||
refetch,
|
||||
response
|
||||
} = useGet<string>({
|
||||
path: `/api/v1/repos/${repoMetadata?.path}/+/${
|
||||
pullRequestMetadata ? `pullreq/${pullRequestMetadata.number}/diff` : `compare/${targetBranch}...${sourceBranch}`
|
||||
@ -118,22 +113,17 @@ export const Changes: React.FC<ChangesProps> = ({
|
||||
const fileId = changedFileId([diff.oldName, diff.newName])
|
||||
const containerId = `container-${fileId}`
|
||||
const contentId = `content-${fileId}`
|
||||
const fileTitle = diff.isDeleted
|
||||
? diff.oldName
|
||||
: diff.isRename
|
||||
? `${diff.oldName} -> ${diff.newName}`
|
||||
: diff.newName
|
||||
const fileActivities: TypesPullReqActivity[] | undefined = activities?.filter(activity => {
|
||||
const payload = activity.payload as PullRequestCodeCommentPayload
|
||||
return payload?.file_id === fileId && payload?.version === PR_CODE_COMMENT_PAYLOAD_VERSION
|
||||
})
|
||||
const filePath = diff.isDeleted ? diff.oldName : diff.newName
|
||||
const fileActivities: TypesPullReqActivity[] | undefined = activities?.filter(
|
||||
activity => filePath === activity.code_comment_path
|
||||
)
|
||||
|
||||
return {
|
||||
...diff,
|
||||
containerId,
|
||||
contentId,
|
||||
fileId,
|
||||
fileTitle,
|
||||
filePath,
|
||||
fileActivities: fileActivities || [],
|
||||
activities: activities || []
|
||||
}
|
||||
@ -217,11 +207,6 @@ export const Changes: React.FC<ChangesProps> = ({
|
||||
pullRequestMetadata={pullRequestMetadata}
|
||||
refreshPr={voidFn(noop)}
|
||||
/>
|
||||
{/* <ReviewDecisionButton
|
||||
repoMetadata={repoMetadata}
|
||||
pullRequestMetadata={pullRequestMetadata}
|
||||
shouldHide={readOnly || pullRequestMetadata?.state === 'merged'}
|
||||
/> */}
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
|
||||
@ -244,6 +229,8 @@ export const Changes: React.FC<ChangesProps> = ({
|
||||
repoMetadata={repoMetadata}
|
||||
pullRequestMetadata={pullRequestMetadata}
|
||||
onCommentUpdate={onCommentUpdate}
|
||||
mergeBaseSHA={response?.headers?.get('x-merge-base-sha') || ''}
|
||||
sourceSHA={response?.headers?.get('x-source-sha') || ''}
|
||||
/>
|
||||
))}
|
||||
</Layout.Vertical>
|
||||
|
@ -310,7 +310,7 @@ const CommentsThread = <T = unknown,>({
|
||||
hideGutter={isLastItem}>
|
||||
<Container
|
||||
padding={{
|
||||
left: editIndexes[index] ? undefined : 'medium',
|
||||
// left: editIndexes[index] ? undefined : 'medium',
|
||||
bottom: isLastItem ? undefined : 'xsmall'
|
||||
}}>
|
||||
<Render when={index === 0 && outlets[CommentBoxOutletPosition.TOP_OF_FIRST_COMMENT]}>
|
||||
|
@ -24,7 +24,7 @@ import type { DiffFileEntry } from 'utils/types'
|
||||
import { useConfirmAct } from 'hooks/useConfirmAction'
|
||||
import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import type { TypesPullReq, TypesPullReqActivity } from 'services/code'
|
||||
import type { OpenapiCommentCreatePullReqRequest, TypesPullReq, TypesPullReqActivity } from 'services/code'
|
||||
import { getErrorMessage } from 'utils/Utils'
|
||||
import { CopyButton } from 'components/CopyButton/CopyButton'
|
||||
import { AppWrapper } from 'App'
|
||||
@ -36,10 +36,6 @@ import {
|
||||
DiffCommentItem,
|
||||
DIFF_VIEWER_HEADER_HEIGHT,
|
||||
getCommentLineInfo,
|
||||
getDiffHTMLSnapshotFromRow,
|
||||
getRawTextInRange,
|
||||
PR_CODE_COMMENT_PAYLOAD_VERSION,
|
||||
PullRequestCodeCommentPayload,
|
||||
renderCommentOppositePlaceHolder,
|
||||
ViewStyle
|
||||
} from './DiffViewerUtils'
|
||||
@ -53,6 +49,8 @@ interface DiffViewerProps extends Pick<GitInfoProps, 'repoMetadata'> {
|
||||
readOnly?: boolean
|
||||
pullRequestMetadata?: TypesPullReq
|
||||
onCommentUpdate: () => void
|
||||
mergeBaseSHA?: string
|
||||
sourceSHA?: string
|
||||
}
|
||||
|
||||
//
|
||||
@ -67,7 +65,9 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
||||
readOnly,
|
||||
repoMetadata,
|
||||
pullRequestMetadata,
|
||||
onCommentUpdate
|
||||
onCommentUpdate,
|
||||
mergeBaseSHA,
|
||||
sourceSHA
|
||||
}) => {
|
||||
const { routes } = useAppContext()
|
||||
const { getString } = useStrings()
|
||||
@ -271,12 +271,10 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
||||
|
||||
const element = commentRowElement.firstElementChild as HTMLTableCellElement
|
||||
|
||||
// Note: 1. CommentBox is rendered as an independent React component
|
||||
// Note: CommentBox is rendered as an independent React component
|
||||
// everything passed to it must be either values, or refs. If you
|
||||
// pass callbacks or states, they won't be updated and might
|
||||
// cause unexpected bugs
|
||||
// 2. If you use a component inside CommentBox, make sure it follow
|
||||
// the above rules as well (i.e useString as a prop instead of importing)
|
||||
ReactDOM.unmountComponentAtNode(element as HTMLDivElement)
|
||||
ReactDOM.render(
|
||||
<AppWrapper>
|
||||
@ -308,22 +306,18 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
||||
|
||||
switch (action) {
|
||||
case CommentAction.NEW: {
|
||||
// lineNumberRange can be used to allow multiple-line selection when commenting in the future
|
||||
const lineNumberRange = [comment.lineNumber]
|
||||
const payload: PullRequestCodeCommentPayload = {
|
||||
type: CommentType.CODE_COMMENT,
|
||||
version: PR_CODE_COMMENT_PAYLOAD_VERSION,
|
||||
file_id: diff.fileId,
|
||||
file_title: diff.fileTitle,
|
||||
language: diff.language || '',
|
||||
is_on_left: comment.left,
|
||||
at_line_number: comment.lineNumber,
|
||||
line_number_range: lineNumberRange,
|
||||
range_text_content: getRawTextInRange(diff, lineNumberRange),
|
||||
diff_html_snapshot: getDiffHTMLSnapshotFromRow(rowElement)
|
||||
const payload: OpenapiCommentCreatePullReqRequest = {
|
||||
line_start: comment.lineNumber,
|
||||
line_end: comment.lineNumber,
|
||||
line_start_new: !comment.left,
|
||||
line_end_new: !comment.left,
|
||||
path: diff.filePath,
|
||||
source_commit_sha: sourceSHA,
|
||||
target_commit_sha: mergeBaseSHA,
|
||||
text: value
|
||||
}
|
||||
|
||||
await saveComment({ type: CommentType.CODE_COMMENT, text: value, payload })
|
||||
await saveComment(payload)
|
||||
.then((newComment: TypesPullReqActivity) => {
|
||||
updatedItem = activityToCommentItem(newComment)
|
||||
})
|
||||
@ -336,7 +330,7 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
||||
|
||||
case CommentAction.REPLY: {
|
||||
const parentComment = diff.fileActivities?.find(
|
||||
activity => (activity.payload as PullRequestCodeCommentPayload).file_id === diff.fileId
|
||||
activity => diff.filePath === activity.code_comment_path
|
||||
)
|
||||
|
||||
if (parentComment) {
|
||||
@ -404,10 +398,6 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
// Comment no longer has UI relevant anchors to be rendered
|
||||
// else {
|
||||
// console.info('Comment is discarded due to no UI relevant anchors', { comment, lineInfo })
|
||||
// }
|
||||
})
|
||||
},
|
||||
[
|
||||
@ -422,7 +412,9 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
||||
updateComment,
|
||||
deleteComment,
|
||||
confirmAct,
|
||||
onCommentUpdate
|
||||
onCommentUpdate,
|
||||
mergeBaseSHA,
|
||||
sourceSHA
|
||||
]
|
||||
)
|
||||
|
||||
@ -472,12 +464,12 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
||||
to={routes.toCODERepository({
|
||||
repoPath: repoMetadata.path as string,
|
||||
gitRef: pullRequestMetadata?.source_branch,
|
||||
resourcePath: diff.fileTitle
|
||||
resourcePath: diff.isRename ? diff.newName : diff.filePath
|
||||
})}>
|
||||
{diff.fileTitle}
|
||||
{diff.isRename ? `${diff.oldName} -> ${diff.newName}` : diff.filePath}
|
||||
</Link>
|
||||
</Text>
|
||||
<CopyButton content={diff.fileTitle} icon={CodeIcon.Copy} size={ButtonSize.SMALL} />
|
||||
<CopyButton content={diff.filePath} icon={CodeIcon.Copy} size={ButtonSize.SMALL} />
|
||||
<FlexExpander />
|
||||
|
||||
<Render when={!readOnly}>
|
||||
|
@ -21,8 +21,9 @@ export enum CommentType {
|
||||
STATE_CHANGE = 'state-change'
|
||||
}
|
||||
|
||||
export const PR_CODE_COMMENT_PAYLOAD_VERSION = '0.1'
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export interface PullRequestCodeCommentPayload {
|
||||
type: CommentType
|
||||
version: string // used to avoid rendering old payload structure
|
||||
@ -200,92 +201,21 @@ export const activityToCommentItem = (activity: TypesPullReqActivity): CommentIt
|
||||
payload: activity
|
||||
})
|
||||
|
||||
/**
|
||||
* Take a small HTML snapshot of a diff in order to render code comment.
|
||||
* @param atRow Row element where the comment is placed.
|
||||
* @param maxNumberOfLines Maximum number of lines to take.
|
||||
* @returns HTML content of the diff.
|
||||
*/
|
||||
export function getDiffHTMLSnapshotFromRow(atRow: HTMLTableRowElement, maxNumberOfLines = 5) {
|
||||
let linesCapturedCount = 0
|
||||
const diffSnapshot = [atRow.outerHTML.trim()]
|
||||
|
||||
let prev = atRow.previousElementSibling
|
||||
|
||||
while (prev && linesCapturedCount < maxNumberOfLines) {
|
||||
if (!prev.hasAttribute('data-annotated-line') && !prev.hasAttribute('data-place-holder-for-line')) {
|
||||
// Don't count empty lines
|
||||
const textContent = prev.textContent?.replace(/\s/g, '')
|
||||
if (textContent?.length && textContent !== '+') {
|
||||
linesCapturedCount++
|
||||
}
|
||||
|
||||
if (textContent !== '+') {
|
||||
diffSnapshot.unshift((prev.outerHTML || '').trim())
|
||||
}
|
||||
}
|
||||
prev = prev.previousElementSibling
|
||||
}
|
||||
|
||||
return diffSnapshot.join('')
|
||||
}
|
||||
|
||||
// export function getDiffHTMLSnapshotFromDiff(diff: DiffFileEntry, lineNumberRange: number[], isOnLeft: boolean) {
|
||||
// const lines = diff?.blocks.reduce((group, item) => {
|
||||
// group = group.concat(item.lines)
|
||||
// return group
|
||||
// }, [] as DiffLine[])
|
||||
|
||||
// const lastIndex = lines.findIndex(line =>
|
||||
// lineNumberRange.includes((isOnLeft ? line.oldNumber : line.newNumber) as number)
|
||||
// )
|
||||
// const startIndex = Math.max(0, lastIndex - 5)
|
||||
|
||||
// const copiedLines = lines.slice(startIndex, lastIndex)
|
||||
// const copiedDiff: DiffFileEntry = {
|
||||
// ...diff,
|
||||
// blocks: [
|
||||
// {
|
||||
// header: '',
|
||||
// lines: copiedLines,
|
||||
// newStartLine: copiedLines[0].newNumber as number,
|
||||
// oldStartLine: copiedLines[0].oldNumber as number
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
|
||||
// // console.log({ isOnLeft, startIndex, lastIndex, copiedLines, lines, lineNumberRange })
|
||||
|
||||
// const div = document.createElement('div')
|
||||
// new Diff2HtmlUI(div, [copiedDiff], Object.assign({}, DIFF2HTML_CONFIG, { outputFormat: 'line-by-line' })).draw()
|
||||
|
||||
// return div.querySelector('table')?.outerHTML || ''
|
||||
// }
|
||||
|
||||
export function getRawTextInRange(diff: DiffFileEntry, lineNumberRange: number[]) {
|
||||
return (
|
||||
// TODO: This is wrong, blocks can have multiple items, not one
|
||||
(diff?.blocks[0]?.lines || [])
|
||||
.filter(line => lineNumberRange.includes(line.newNumber as number))
|
||||
.map(line => line.content)
|
||||
.join('\n') || ''
|
||||
)
|
||||
}
|
||||
|
||||
export function activitiesToDiffCommentItems(diff: DiffFileEntry): DiffCommentItem<TypesPullReqActivity>[] {
|
||||
return (
|
||||
diff.fileActivities?.map(activity => {
|
||||
const payload = activity.payload as PullRequestCodeCommentPayload
|
||||
const replyComments =
|
||||
diff.activities
|
||||
?.filter(replyActivity => replyActivity.parent_id === activity.id)
|
||||
.map(_activity => activityToCommentItem(_activity)) || []
|
||||
// TODO: Use backend support when it's ready https://harness.slack.com/archives/C03Q1Q4C9J8/p1682609265294089
|
||||
const left = activity.payload?.line_start_new || false
|
||||
|
||||
return {
|
||||
left: payload.is_on_left,
|
||||
right: !payload.is_on_left,
|
||||
left,
|
||||
right: !left,
|
||||
height: 0,
|
||||
lineNumber: payload.at_line_number,
|
||||
lineNumber: (left ? activity.code_comment_line_old : activity.code_comment_line_new) as number,
|
||||
commentItems: [activityToCommentItem(activity)].concat(replyComments)
|
||||
}
|
||||
}) || []
|
||||
|
@ -91,10 +91,6 @@
|
||||
.anchor {
|
||||
display: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
@import 'src/utils/utils';
|
||||
|
||||
.main {
|
||||
overflow: auto;
|
||||
|
||||
:global {
|
||||
.wmde-markdown {
|
||||
@include markdown-font;
|
||||
|
||||
pre {
|
||||
position: relative;
|
||||
|
||||
@ -11,6 +15,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
tt,
|
||||
code {
|
||||
@include mono-font;
|
||||
}
|
||||
|
||||
// Customize https://wangchujiang.com/rehype-video/
|
||||
details.octicon.octicon-video {
|
||||
display: block;
|
||||
|
@ -63,7 +63,8 @@ export const OptionsMenuButton = ({
|
||||
item as IMenuItemProps & React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||
'isDanger',
|
||||
'hasIcon',
|
||||
'iconName'
|
||||
'iconName',
|
||||
'iconSize'
|
||||
)}
|
||||
/>
|
||||
)
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { Container } from '@harness/uicore'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import type monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
|
||||
import MonacoEditor from 'react-monaco-editor'
|
||||
import MonacoEditor, { MonacoDiffEditor } from 'react-monaco-editor'
|
||||
import { noop } from 'lodash-es'
|
||||
import { SourceCodeEditorProps, PLAIN_TEXT } from 'utils/Utils'
|
||||
import { useEventListener } from 'hooks/useEventListener'
|
||||
|
||||
export const MonacoEditorOptions = {
|
||||
ignoreTrimWhitespace: true,
|
||||
@ -58,12 +58,12 @@ export default function MonacoSourceCodeEditor({
|
||||
language = PLAIN_TEXT,
|
||||
lineNumbers = true,
|
||||
readOnly = false,
|
||||
className,
|
||||
height,
|
||||
autoHeight,
|
||||
wordWrap = true,
|
||||
onChange = noop
|
||||
}: SourceCodeEditorProps) {
|
||||
const [editor, setEditor] = useState<monacoEditor.editor.IStandaloneCodeEditor>()
|
||||
const scrollbar = autoHeight ? 'hidden' : 'auto'
|
||||
|
||||
useEffect(() => {
|
||||
@ -72,31 +72,80 @@ export default function MonacoSourceCodeEditor({
|
||||
monaco.languages.typescript?.typescriptDefaults?.setCompilerOptions?.(compilerOptions)
|
||||
}, [])
|
||||
|
||||
useEventListener('resize', () => {
|
||||
editor?.layout({ width: 0, height: 0 })
|
||||
window.requestAnimationFrame(() => editor?.layout())
|
||||
})
|
||||
|
||||
return (
|
||||
<Container className={className}>
|
||||
<MonacoEditor
|
||||
language={language}
|
||||
theme="vs-light"
|
||||
value={source}
|
||||
height={height}
|
||||
options={{
|
||||
...MonacoEditorOptions,
|
||||
readOnly,
|
||||
wordWrap: toOnOff(wordWrap),
|
||||
lineNumbers: toOnOff(lineNumbers),
|
||||
scrollbar: {
|
||||
vertical: scrollbar,
|
||||
horizontal: scrollbar,
|
||||
alwaysConsumeMouseWheel: false
|
||||
}
|
||||
}}
|
||||
editorDidMount={_editor => {
|
||||
if (autoHeight) {
|
||||
autoAdjustEditorHeight(_editor)
|
||||
}
|
||||
}}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</Container>
|
||||
<MonacoEditor
|
||||
language={language}
|
||||
theme="vs-light"
|
||||
value={source}
|
||||
height={height}
|
||||
options={{
|
||||
...MonacoEditorOptions,
|
||||
readOnly,
|
||||
wordWrap: toOnOff(wordWrap),
|
||||
lineNumbers: toOnOff(lineNumbers),
|
||||
scrollbar: {
|
||||
vertical: scrollbar,
|
||||
horizontal: scrollbar,
|
||||
alwaysConsumeMouseWheel: false
|
||||
}
|
||||
}}
|
||||
editorDidMount={_editor => {
|
||||
if (autoHeight) {
|
||||
autoAdjustEditorHeight(_editor)
|
||||
}
|
||||
setEditor(_editor)
|
||||
}}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
interface DiffEditorProps extends Omit<SourceCodeEditorProps, 'autoHeight'> {
|
||||
original: string
|
||||
}
|
||||
|
||||
export function DiffEditor({
|
||||
source,
|
||||
original,
|
||||
language = PLAIN_TEXT,
|
||||
lineNumbers = true,
|
||||
readOnly = false,
|
||||
height,
|
||||
wordWrap = true,
|
||||
onChange = noop
|
||||
}: DiffEditorProps) {
|
||||
const [editor, setEditor] = useState<monacoEditor.editor.IStandaloneDiffEditor>()
|
||||
|
||||
useEventListener('resize', () => {
|
||||
editor?.layout({ width: 0, height: 0 })
|
||||
window.requestAnimationFrame(() => editor?.layout())
|
||||
})
|
||||
|
||||
return (
|
||||
<MonacoDiffEditor
|
||||
language={language}
|
||||
theme="vs-light"
|
||||
original={original}
|
||||
value={source}
|
||||
height={height}
|
||||
options={{
|
||||
...MonacoEditorOptions,
|
||||
readOnly,
|
||||
wordWrap: toOnOff(wordWrap),
|
||||
lineNumbers: toOnOff(lineNumbers),
|
||||
scrollbar: {
|
||||
vertical: 'auto',
|
||||
horizontal: 'auto',
|
||||
alwaysConsumeMouseWheel: false
|
||||
}
|
||||
}}
|
||||
editorDidMount={setEditor}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ export interface StringsMap {
|
||||
branches: string
|
||||
browse: string
|
||||
cancel: string
|
||||
changes: string
|
||||
checkRuns: string
|
||||
checkSuites: string
|
||||
checks: string
|
||||
@ -62,6 +63,7 @@ export interface StringsMap {
|
||||
confirmDeleteWebhook: string
|
||||
confirmation: string
|
||||
content: string
|
||||
contents: string
|
||||
conversation: string
|
||||
copy: string
|
||||
copyCommitSHA: string
|
||||
|
@ -192,7 +192,7 @@ pr:
|
||||
failedToDeleteComment: Failed to delete comment. Please try again.
|
||||
prMerged: This Pull Request was merged
|
||||
reviewSubmitted: Review submitted.
|
||||
prReviewSubmit: '{user} {state|approved:approved, rejected:rejected,changereq:requested changes to, reviewed} the pull request. {time}'
|
||||
prReviewSubmit: '{user} {state|approved:approved, rejected:rejected,changereq:requested to, reviewed} the pull request. {time}'
|
||||
prMergedInfo: '{user} merged branch {source} into {target} {time}.'
|
||||
prBranchPushInfo: '{user} pushed a new commit {commit}.'
|
||||
prStateChanged: '{user} changed pull request state from {old} to {new}.'
|
||||
@ -375,3 +375,5 @@ manageCredText: You can also manage your git credential {URL}
|
||||
blame: Blame
|
||||
viewRaw: View Raw
|
||||
download: Download
|
||||
changes: Changes
|
||||
contents: Contents
|
||||
|
@ -83,6 +83,7 @@
|
||||
.snapshot {
|
||||
--border-color: var(--grey-200);
|
||||
--radius: 4px;
|
||||
margin-top: var(--spacing-small) !important;
|
||||
|
||||
.title {
|
||||
display: grid;
|
||||
@ -110,6 +111,10 @@
|
||||
border-bottom-right-radius: var(--radius);
|
||||
|
||||
:global {
|
||||
.d2h-file-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.d2h-wrapper > div {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
@ -19,8 +19,10 @@ import {
|
||||
} from '@harness/uicore'
|
||||
import cx from 'classnames'
|
||||
import { useGet, useMutate } from 'restful-react'
|
||||
import { Diff2HtmlUI } from 'diff2html/lib-esm/ui/js/diff2html-ui'
|
||||
import ReactTimeago from 'react-timeago'
|
||||
import { orderBy } from 'lodash-es'
|
||||
import * as Diff2Html from 'diff2html'
|
||||
import { get, orderBy } from 'lodash-es'
|
||||
import { Render } from 'react-jsx-match'
|
||||
import { CodeIcon, GitInfoProps } from 'utils/GitUtils'
|
||||
import { MarkdownViewer } from 'components/MarkdownViewer/MarkdownViewer'
|
||||
@ -30,11 +32,7 @@ import type { TypesPullReqActivity } from 'services/code'
|
||||
import { CommentAction, CommentBox, CommentBoxOutletPosition, CommentItem } from 'components/CommentBox/CommentBox'
|
||||
import { useConfirmAct } from 'hooks/useConfirmAction'
|
||||
import { commentState, formatDate, formatTime, getErrorMessage, orderSortDate, dayAgoInMS } from 'utils/Utils'
|
||||
import {
|
||||
activityToCommentItem,
|
||||
CommentType,
|
||||
PullRequestCodeCommentPayload
|
||||
} from 'components/DiffViewer/DiffViewerUtils'
|
||||
import { activityToCommentItem, CommentType, DIFF2HTML_CONFIG, ViewStyle } from 'components/DiffViewer/DiffViewerUtils'
|
||||
import { ThreadSection } from 'components/ThreadSection/ThreadSection'
|
||||
import { PullRequestTabContentWrapper } from '../PullRequestTabContentWrapper'
|
||||
import { DescriptionBox } from './DescriptionBox'
|
||||
@ -447,7 +445,7 @@ export const Conversation: React.FC<ConversationProps> = ({
|
||||
}
|
||||
|
||||
function isCodeComment(commentItems: CommentItem<TypesPullReqActivity>[]) {
|
||||
return (commentItems[0]?.payload?.payload as Unknown)?.type === CommentType.CODE_COMMENT
|
||||
return commentItems[0]?.payload?.type === CommentType.CODE_COMMENT
|
||||
}
|
||||
|
||||
interface CodeCommentHeaderProps {
|
||||
@ -455,39 +453,41 @@ interface CodeCommentHeaderProps {
|
||||
}
|
||||
|
||||
const CodeCommentHeader: React.FC<CodeCommentHeaderProps> = ({ commentItems }) => {
|
||||
if (isCodeComment(commentItems)) {
|
||||
const payload = commentItems[0]?.payload?.payload as PullRequestCodeCommentPayload
|
||||
const _isCodeComment = isCodeComment(commentItems)
|
||||
const id = `code-comment-snapshot-${commentItems[0]?.payload?.code_comment_path}`
|
||||
|
||||
return (
|
||||
<Container className={css.snapshot}>
|
||||
<Layout.Vertical>
|
||||
<Container className={css.title}>
|
||||
<Text inline className={css.fname}>
|
||||
{payload?.file_title}
|
||||
</Text>
|
||||
</Container>
|
||||
<Container className={css.snapshotContent}>
|
||||
<Container className="d2h-wrapper">
|
||||
<Container className="d2h-file-wrapper line-by-line-file-diff">
|
||||
<Container className="d2h-file-diff">
|
||||
<Container className="d2h-code-wrapper">
|
||||
<table className="d2h-diff-table" cellPadding="0px" cellSpacing="0px">
|
||||
<tbody
|
||||
className="d2h-diff-tbody"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: payload?.diff_html_snapshot || ''
|
||||
}}></tbody>
|
||||
</table>
|
||||
</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
</Layout.Vertical>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
return null
|
||||
useEffect(() => {
|
||||
if (_isCodeComment) {
|
||||
const codeDiffSnapshot = [
|
||||
`diff --git a/hello-world.md b/hello-world.md`,
|
||||
`new file mode 100644`,
|
||||
'index 0000000..0000000',
|
||||
'--- /dev/null',
|
||||
'+++ b/hello-world.md',
|
||||
get(commentItems[0], 'payload.payload.title', ''),
|
||||
...get(commentItems[0], 'payload.payload.lines', [])
|
||||
].join('\n')
|
||||
|
||||
new Diff2HtmlUI(
|
||||
document.getElementById(id) as HTMLElement,
|
||||
Diff2Html.parse(codeDiffSnapshot, DIFF2HTML_CONFIG),
|
||||
Object.assign({}, DIFF2HTML_CONFIG, { outputFormat: ViewStyle.LINE_BY_LINE })
|
||||
).draw()
|
||||
}
|
||||
}, [id, commentItems, _isCodeComment])
|
||||
|
||||
return _isCodeComment ? (
|
||||
<Container className={css.snapshot}>
|
||||
<Layout.Vertical>
|
||||
<Container className={css.title}>
|
||||
<Text inline className={css.fname}>
|
||||
{commentItems[0].payload?.code_comment_path}
|
||||
</Text>
|
||||
</Container>
|
||||
<Container className={css.snapshotContent} id={id} />
|
||||
</Layout.Vertical>
|
||||
</Container>
|
||||
) : null
|
||||
}
|
||||
|
||||
function isSystemComment(commentItems: CommentItem<TypesPullReqActivity>[]) {
|
||||
|
@ -1,9 +1,13 @@
|
||||
.container {
|
||||
--header-height: 96px;
|
||||
--heading-height: 58px;
|
||||
--tabs-height: 36px;
|
||||
|
||||
background-color: var(--white) !important;
|
||||
margin-top: 0 !important;
|
||||
// box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16);
|
||||
// border-radius: 4px;
|
||||
height: calc(100vh - 96px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
overflow: hidden;
|
||||
|
||||
.heading {
|
||||
@ -11,7 +15,7 @@
|
||||
// border-top-right-radius: 4px;
|
||||
align-items: center;
|
||||
padding: 0 var(--spacing-xlarge) !important;
|
||||
height: 58px;
|
||||
height: var(--heading-height);
|
||||
background-color: var(--grey-100);
|
||||
box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16);
|
||||
border-bottom: 1px solid var(--grey-200);
|
||||
@ -40,13 +44,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
.tabs {
|
||||
height: var(--tabs-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--grey-50);
|
||||
--tab-height: 18px;
|
||||
|
||||
.selectedView {
|
||||
height: var(--tab-height);
|
||||
|
||||
> div {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: var(--tab-height) !important;
|
||||
width: auto;
|
||||
padding: 0 var(--spacing-large);
|
||||
font-weight: 700;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
|
||||
&[class*='selected'] {
|
||||
background-color: var(--primary-9);
|
||||
}
|
||||
|
||||
&:not([class*='selected']) {
|
||||
color: var(--primary-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.editorContainer {
|
||||
padding-left: var(--spacing-medium) !important;
|
||||
overflow: hidden;
|
||||
|
||||
.editorContainer {
|
||||
height: calc(100vh - 96px - 58px);
|
||||
overflow: hidden;
|
||||
}
|
||||
height: calc(100vh - var(--header-height) - var(--heading-height) - var(--tabs-height));
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ declare const styles: {
|
||||
readonly path: string
|
||||
readonly inputContainer: string
|
||||
readonly refLink: string
|
||||
readonly content: string
|
||||
readonly tabs: string
|
||||
readonly selectedView: string
|
||||
readonly editorContainer: string
|
||||
}
|
||||
export default styles
|
||||
|
@ -1,5 +1,17 @@
|
||||
import React, { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Button, ButtonVariation, Color, Container, FlexExpander, Icon, Layout, Text, TextInput } from '@harness/uicore'
|
||||
import {
|
||||
Button,
|
||||
ButtonVariation,
|
||||
Color,
|
||||
Container,
|
||||
FlexExpander,
|
||||
Icon,
|
||||
Layout,
|
||||
Text,
|
||||
TextInput,
|
||||
VisualYamlSelectedView,
|
||||
VisualYamlToggle
|
||||
} from '@harness/uicore'
|
||||
import { Link, useHistory } from 'react-router-dom'
|
||||
import ReactJoin from 'react-join'
|
||||
import cx from 'classnames'
|
||||
@ -11,6 +23,7 @@ import { useStrings } from 'framework/strings'
|
||||
import { filenameToLanguage, FILE_SEPERATOR } from 'utils/Utils'
|
||||
import { useGetResourceContent } from 'hooks/useGetResourceContent'
|
||||
import { CommitModalButton } from 'components/CommitModalButton/CommitModalButton'
|
||||
import { DiffEditor } from 'components/SourceCodeEditor/MonacoSourceCodeEditor'
|
||||
import css from './FileEditor.module.scss'
|
||||
|
||||
interface EditorProps extends Pick<GitInfoProps, 'repoMetadata' | 'gitRef' | 'resourcePath'> {
|
||||
@ -83,6 +96,7 @@ function Editor({ resourceContent, repoMetadata, gitRef, resourcePath, isReposit
|
||||
// Make API call to verify if fileResourcePath is an existing folder
|
||||
verifyFolder().then(() => setStartVerifyFolder(true))
|
||||
}, [fileName, parentPath, language, content, verifyFolder])
|
||||
const [selectedView, setSelectedView] = useState(VisualYamlSelectedView.VISUAL)
|
||||
|
||||
// Calculate file name input field width based on number of characters inside
|
||||
useEffect(() => {
|
||||
@ -111,16 +125,14 @@ function Editor({ resourceContent, repoMetadata, gitRef, resourcePath, isReposit
|
||||
}
|
||||
}
|
||||
}, [isNew, name])
|
||||
|
||||
return (
|
||||
<Container className={css.container}>
|
||||
<Layout.Horizontal className={css.heading}>
|
||||
<Container>
|
||||
<Layout.Horizontal spacing="small" className={css.path}>
|
||||
<Link to={routes.toCODERepository({ repoPath: repoMetadata.path as string, gitRef })}>
|
||||
<Icon name="main-folder" padding={{ right: 'xsmall' }} />
|
||||
{/* <Text color={Color.GREY_900} inline>
|
||||
{repoMetadata.uid}
|
||||
</Text> */}
|
||||
<Icon name="code-folder" padding={{ right: 'xsmall' }} />
|
||||
</Link>
|
||||
<PathSeparator />
|
||||
{parentPath && (
|
||||
@ -148,7 +160,7 @@ function Editor({ resourceContent, repoMetadata, gitRef, resourcePath, isReposit
|
||||
<TextInput
|
||||
autoFocus={isNew}
|
||||
value={fileName}
|
||||
inputRef={ref => (inputRef.current = ref)}
|
||||
inputRef={_ref => (inputRef.current = _ref)}
|
||||
wrapperClassName={css.inputContainer}
|
||||
placeholder={getString('nameYourFile')}
|
||||
onInput={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
@ -231,15 +243,22 @@ function Editor({ resourceContent, repoMetadata, gitRef, resourcePath, isReposit
|
||||
</Container>
|
||||
</Layout.Horizontal>
|
||||
|
||||
<Container className={cx(css.content, language)}>
|
||||
<SourceCodeEditor
|
||||
className={css.editorContainer}
|
||||
height="100%"
|
||||
language={language}
|
||||
source={originalContent}
|
||||
onChange={setContent}
|
||||
<Container className={css.tabs}>
|
||||
<VisualYamlToggle
|
||||
onChange={setSelectedView}
|
||||
selectedView={selectedView}
|
||||
labels={{ visual: getString('contents'), yaml: getString('changes') }}
|
||||
className={css.selectedView}
|
||||
/>
|
||||
</Container>
|
||||
|
||||
<Container className={cx(css.editorContainer, language)}>
|
||||
{selectedView === VisualYamlSelectedView.VISUAL ? (
|
||||
<SourceCodeEditor language={language} source={content} onChange={setContent} />
|
||||
) : (
|
||||
<DiffEditor language={language} original={originalContent} source={content} onChange={setContent} />
|
||||
)}
|
||||
</Container>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ export interface TypesPullReq {
|
||||
description?: string
|
||||
edited?: number
|
||||
is_draft?: boolean
|
||||
merge_base_sha?: string | null
|
||||
merge_base_sha?: string
|
||||
merge_check_status?: EnumMergeCheckStatus
|
||||
merge_conflicts?: string | null
|
||||
merge_method?: EnumMergeMethod
|
||||
|
@ -4344,7 +4344,6 @@ components:
|
||||
is_draft:
|
||||
type: boolean
|
||||
merge_base_sha:
|
||||
nullable: true
|
||||
type: string
|
||||
merge_check_status:
|
||||
$ref: '#/components/schemas/EnumMergeCheckStatus'
|
||||
|
@ -49,7 +49,6 @@ export interface SourceCodeEditorProps {
|
||||
lineNumbers?: boolean
|
||||
readOnly?: boolean
|
||||
highlightLines?: string // i.e: {1,3-4}, TODO: not yet supported
|
||||
className?: string
|
||||
height?: number | string
|
||||
autoHeight?: boolean
|
||||
wordWrap?: boolean
|
||||
|
@ -3,7 +3,7 @@ import type { TypesPullReqActivity } from 'services/code'
|
||||
|
||||
export interface DiffFileEntry extends DiffFile {
|
||||
fileId: string
|
||||
fileTitle: string
|
||||
filePath: string
|
||||
containerId: string
|
||||
contentId: string
|
||||
fileActivities?: TypesPullReqActivity[]
|
||||
|
@ -3,7 +3,7 @@ $code-editor-font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menl
|
||||
|
||||
@mixin mono-font {
|
||||
font-family: var(--font-family-mono) !important;
|
||||
font-size: 12px !important;
|
||||
font-size: 13px !important;
|
||||
font-feature-settings: 'liga' 0, 'calt' 0;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0px;
|
||||
@ -11,5 +11,5 @@ $code-editor-font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menl
|
||||
|
||||
@mixin markdown-font {
|
||||
font-family: var(--font-family) !important;
|
||||
font-size: 12px !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user