3.3 KB105 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import {Banner, BannerKind} from 'isl-components/Banner';
9import {Button} from 'isl-components/Button';
10import {Icon} from 'isl-components/Icon';
11import {useAtom, useAtomValue} from 'jotai';
12import clientToServerAPI from '../ClientToServerAPI';
13import {T} from '../i18n';
14import {latestHeadCommit} from '../serverAPIState';
15import {codeReviewStatusAtom} from './firstPassCodeReviewAtoms';
16
17import {Tooltip} from 'isl-components/Tooltip';
18import {useEffect, useState} from 'react';
19import {tracker} from '../analytics';
20import {useFeatureFlagSync} from '../featureFlags';
21import {Internal} from '../Internal';
22import platform from '../platform';
23import './AICodeReviewUpsell.css';
24
25export function AICodeReviewUpsell({
26 isCommitMode,
27 hasUncommittedChanges,
28}: {
29 isCommitMode: boolean;
30 hasUncommittedChanges: boolean;
31}): JSX.Element | null {
32 const [status, setStatus] = useAtom(codeReviewStatusAtom);
33 const headCommit = useAtomValue(latestHeadCommit);
34 const [hidden, setHidden] = useState(false);
35 const aiFirstPassCodeReviewEnabled = useFeatureFlagSync(
36 Internal.featureFlags?.AIFirstPassCodeReview,
37 );
38
39 useEffect(() => {
40 setHidden(false);
41 }, [headCommit]);
42
43 // TODO: move this component to vscode/webview
44 if (platform.platformName !== 'vscode') {
45 return null;
46 }
47
48 if (hidden) {
49 return null;
50 }
51
52 const codeReviewProvider = Internal.aiCodeReview?.provider ?? 'AI';
53 const bannerText = isCommitMode
54 ? `Use ${codeReviewProvider} to review your uncommitted changes`
55 : hasUncommittedChanges
56 ? `Use ${codeReviewProvider} to review this commit and your uncommitted changes`
57 : `Use ${codeReviewProvider} to review this commit`;
58
59 const isReviewInProgress = aiFirstPassCodeReviewEnabled && status === 'running';
60 const noUncommittedChanges = isCommitMode && !hasUncommittedChanges;
61 const shouldDisableButton = isReviewInProgress || noUncommittedChanges;
62 const disabledReason = isReviewInProgress
63 ? 'Review already in progress'
64 : noUncommittedChanges
65 ? 'No uncommitted changes'
66 : null;
67
68 const button = (
69 <Button // TODO: Replace with dropdown to choose between quick/thorough review
70 onClick={() => {
71 setStatus('running');
72 setHidden(true);
73 clientToServerAPI.postMessage({
74 type: 'platform/runAICodeReviewChat',
75 reviewScope: isCommitMode
76 ? 'uncommitted changes'
77 : hasUncommittedChanges
78 ? 'current commit and uncommitted changes'
79 : 'current commit',
80 source: 'commitInfoView',
81 });
82 tracker.track('AICodeReviewInitiatedFromISL');
83 }}
84 disabled={shouldDisableButton}>
85 {<T>Start review</T>}
86 </Button>
87 );
88
89 return (
90 <Banner kind={BannerKind.default}>
91 <div className="code-review-upsell-inner">
92 <div className="code-review-upsell-icon-text">
93 <Icon icon="sparkle" />
94 {bannerText}
95 </div>
96 {shouldDisableButton && disabledReason ? (
97 <Tooltip title={disabledReason}>{button}</Tooltip>
98 ) : (
99 button
100 )}
101 </div>
102 </Banner>
103 );
104}
105