addons/isl/src/firstPassCodeReview/AICodeReviewStatus.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import {useAtomValue} from 'jotai';
b69ab319import clientToServerAPI from '../ClientToServerAPI';
b69ab3110import {T} from '../i18n';
b69ab3111import {writeAtom} from '../jotaiUtils';
b69ab3112
b69ab3113import {ErrorNotice} from 'isl-components/ErrorNotice';
b69ab3114import {Icon} from 'isl-components/Icon';
b69ab3115import {minimalDisambiguousPaths} from 'shared/minimalDisambiguousPaths';
b69ab3116import {tracker} from '../analytics';
b69ab3117import {Collapsable} from '../Collapsable';
b69ab3118import {relativePath} from '../CwdSelector';
b69ab3119import {Link} from '../Link';
b69ab3120import platform from '../platform';
b69ab3121import {repoRelativeCwd} from '../repositoryData';
b69ab3122import {registerDisposable} from '../utils';
b69ab3123import './AICodeReviewStatus.css';
b69ab3124import {
b69ab3125 codeReviewStatusAtom,
b69ab3126 commentsByFilePathAtom,
b69ab3127 firstPassCommentData,
b69ab3128 firstPassCommentDataCount,
b69ab3129 firstPassCommentError,
b69ab3130} from './firstPassCodeReviewAtoms';
b69ab3131
b69ab3132registerDisposable(
b69ab3133 firstPassCommentData,
b69ab3134 clientToServerAPI.onMessageOfType('platform/gotAIReviewComments', data => {
b69ab3135 const result = data.comments;
b69ab3136 if (result.error) {
b69ab3137 writeAtom(codeReviewStatusAtom, 'error');
b69ab3138 writeAtom(firstPassCommentError, result.error);
b69ab3139 tracker.error('AICodeReviewCompleted', 'AICodeReviewError', result.error);
b69ab3140 } else {
b69ab3141 writeAtom(codeReviewStatusAtom, 'success');
b69ab3142 tracker.track('AICodeReviewCompleted', {extras: {commentCount: result.value.length}});
b69ab3143 }
b69ab3144 }),
b69ab3145 import.meta.hot,
b69ab3146);
b69ab3147
b69ab3148export function AICodeReviewStatus(): JSX.Element | null {
b69ab3149 const repoRoot = useAtomValue(repoRelativeCwd);
b69ab3150 const status = useAtomValue(codeReviewStatusAtom);
b69ab3151 const commentCount = useAtomValue(firstPassCommentDataCount);
b69ab3152 const commentsByFilePath = useAtomValue(commentsByFilePathAtom);
b69ab3153 const error = useAtomValue(firstPassCommentError);
b69ab3154 const disambiguatedPaths = minimalDisambiguousPaths(Object.keys(commentsByFilePath));
b69ab3155
b69ab3156 // TODO: move this component to vscode/webview
b69ab3157 if (platform.platformName !== 'vscode' || status == null) {
b69ab3158 return null;
b69ab3159 }
b69ab3160
b69ab3161 return (
b69ab3162 <Collapsable
b69ab3163 className="comment-collapsable"
b69ab3164 title={
b69ab3165 <div className="comment-collapsible-title">
b69ab3166 <b>
b69ab3167 <T>Devmate Code Review</T>
b69ab3168 </b>
b69ab3169 <div className="comment-collapsible-title-status">
b69ab3170 {status === 'success' &&
b69ab3171 (commentCount > 0 ? (
b69ab3172 <div className="comment-count">
b69ab3173 {commentCount}
b69ab3174 <Icon icon="comment" />
b69ab3175 </div>
b69ab3176 ) : (
b69ab3177 <Icon icon="check" />
b69ab3178 ))}
b69ab3179 {status === 'error' && <Icon icon="error" color="red" />}
b69ab3180 {status === 'running' && <Icon icon="loading" />}
b69ab3181 </div>
b69ab3182 </div>
b69ab3183 }>
b69ab3184 <div className="comment-content-container">
b69ab3185 {status === 'running' && (
b69ab3186 <div className="comment-loading">Devmate is reviewing your code...</div>
b69ab3187 )}
b69ab3188 {status === 'success' &&
b69ab3189 (commentCount > 0 ? (
b69ab3190 <div className="comment-list">
b69ab3191 {Object.entries(commentsByFilePath).map(([filepath, comments], i) =>
b69ab3192 comments.map((comment, j) => (
b69ab3193 <div className="comment-container" key={comment.issueID || `${filepath}-${j}`}>
b69ab3194 <div className="comment-header">
b69ab3195 <Link
b69ab3196 onClick={() =>
b69ab3197 platform.openFile(relativePath(repoRoot, filepath), {
b69ab3198 line: comment.startLine,
b69ab3199 })
b69ab31100 }>
b69ab31101 <b>
b69ab31102 {disambiguatedPaths[i]}:{comment.startLine}
b69ab31103 </b>
b69ab31104 </Link>
b69ab31105 </div>
b69ab31106 <div className="comment-body">
b69ab31107 <T>{comment.description}</T>
b69ab31108 </div>
b69ab31109 </div>
b69ab31110 )),
b69ab31111 )}
b69ab31112 </div>
b69ab31113 ) : (
b69ab31114 <div>
b69ab31115 <T>Everything looks good! Devmate didn't find any issues.</T>
b69ab31116 </div>
b69ab31117 ))}
b69ab31118 {status === 'error' && <ErrorNotice title="Failed to load comments" error={error} />}
b69ab31119 </div>
b69ab31120 </Collapsable>
b69ab31121 );
b69ab31122}