addons/isl/src/firstPassCodeReview/AICodeReviewUpsell.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 {Banner, BannerKind} from 'isl-components/Banner';
b69ab319import {Button} from 'isl-components/Button';
b69ab3110import {Icon} from 'isl-components/Icon';
b69ab3111import {useAtom, useAtomValue} from 'jotai';
b69ab3112import clientToServerAPI from '../ClientToServerAPI';
b69ab3113import {T} from '../i18n';
b69ab3114import {latestHeadCommit} from '../serverAPIState';
b69ab3115import {codeReviewStatusAtom} from './firstPassCodeReviewAtoms';
b69ab3116
b69ab3117import {Tooltip} from 'isl-components/Tooltip';
b69ab3118import {useEffect, useState} from 'react';
b69ab3119import {tracker} from '../analytics';
b69ab3120import {useFeatureFlagSync} from '../featureFlags';
b69ab3121import {Internal} from '../Internal';
b69ab3122import platform from '../platform';
b69ab3123import './AICodeReviewUpsell.css';
b69ab3124
b69ab3125export function AICodeReviewUpsell({
b69ab3126 isCommitMode,
b69ab3127 hasUncommittedChanges,
b69ab3128}: {
b69ab3129 isCommitMode: boolean;
b69ab3130 hasUncommittedChanges: boolean;
b69ab3131}): JSX.Element | null {
b69ab3132 const [status, setStatus] = useAtom(codeReviewStatusAtom);
b69ab3133 const headCommit = useAtomValue(latestHeadCommit);
b69ab3134 const [hidden, setHidden] = useState(false);
b69ab3135 const aiFirstPassCodeReviewEnabled = useFeatureFlagSync(
b69ab3136 Internal.featureFlags?.AIFirstPassCodeReview,
b69ab3137 );
b69ab3138
b69ab3139 useEffect(() => {
b69ab3140 setHidden(false);
b69ab3141 }, [headCommit]);
b69ab3142
b69ab3143 // TODO: move this component to vscode/webview
b69ab3144 if (platform.platformName !== 'vscode') {
b69ab3145 return null;
b69ab3146 }
b69ab3147
b69ab3148 if (hidden) {
b69ab3149 return null;
b69ab3150 }
b69ab3151
b69ab3152 const codeReviewProvider = Internal.aiCodeReview?.provider ?? 'AI';
b69ab3153 const bannerText = isCommitMode
b69ab3154 ? `Use ${codeReviewProvider} to review your uncommitted changes`
b69ab3155 : hasUncommittedChanges
b69ab3156 ? `Use ${codeReviewProvider} to review this commit and your uncommitted changes`
b69ab3157 : `Use ${codeReviewProvider} to review this commit`;
b69ab3158
b69ab3159 const isReviewInProgress = aiFirstPassCodeReviewEnabled && status === 'running';
b69ab3160 const noUncommittedChanges = isCommitMode && !hasUncommittedChanges;
b69ab3161 const shouldDisableButton = isReviewInProgress || noUncommittedChanges;
b69ab3162 const disabledReason = isReviewInProgress
b69ab3163 ? 'Review already in progress'
b69ab3164 : noUncommittedChanges
b69ab3165 ? 'No uncommitted changes'
b69ab3166 : null;
b69ab3167
b69ab3168 const button = (
b69ab3169 <Button // TODO: Replace with dropdown to choose between quick/thorough review
b69ab3170 onClick={() => {
b69ab3171 setStatus('running');
b69ab3172 setHidden(true);
b69ab3173 clientToServerAPI.postMessage({
b69ab3174 type: 'platform/runAICodeReviewChat',
b69ab3175 reviewScope: isCommitMode
b69ab3176 ? 'uncommitted changes'
b69ab3177 : hasUncommittedChanges
b69ab3178 ? 'current commit and uncommitted changes'
b69ab3179 : 'current commit',
b69ab3180 source: 'commitInfoView',
b69ab3181 });
b69ab3182 tracker.track('AICodeReviewInitiatedFromISL');
b69ab3183 }}
b69ab3184 disabled={shouldDisableButton}>
b69ab3185 {<T>Start review</T>}
b69ab3186 </Button>
b69ab3187 );
b69ab3188
b69ab3189 return (
b69ab3190 <Banner kind={BannerKind.default}>
b69ab3191 <div className="code-review-upsell-inner">
b69ab3192 <div className="code-review-upsell-icon-text">
b69ab3193 <Icon icon="sparkle" />
b69ab3194 {bannerText}
b69ab3195 </div>
b69ab3196 {shouldDisableButton && disabledReason ? (
b69ab3197 <Tooltip title={disabledReason}>{button}</Tooltip>
b69ab3198 ) : (
b69ab3199 button
b69ab31100 )}
b69ab31101 </div>
b69ab31102 </Banner>
b69ab31103 );
b69ab31104}