Skip to content

Commit 4ee3eaf

Browse files
Improve credits message
1 parent 21c6020 commit 4ee3eaf

File tree

4 files changed

+115
-43
lines changed

4 files changed

+115
-43
lines changed

newIDE/app/src/AiGeneration/AiRequestChat/ChatMessages.js

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,6 @@ export const ChatMessages: React.ComponentType<Props> = React.memo<Props>(
969969
undefined
970970
) : (
971971
<div className={classes.feedbackButtonsContainer}>
972-
{/* $FlowFixMe[constant-condition] */}
973972
{isLastMessage && shouldDisplayFeedbackBanner && (
974973
<Text
975974
size="body-small"
@@ -1054,7 +1053,6 @@ export const ChatMessages: React.ComponentType<Props> = React.memo<Props>(
10541053
}
10551054
>
10561055
<Column noMargin>
1057-
{/* $FlowFixMe[constant-condition] */}
10581056
{functionCallItems && functionCallItems.length > 0 && (
10591057
<FunctionCallsGroup>
10601058
{functionCallItems.map(
@@ -1135,8 +1133,7 @@ export const ChatMessages: React.ComponentType<Props> = React.memo<Props>(
11351133
<Trans>Restoring...</Trans>
11361134
</Text>
11371135
</LineStackLayout>
1138-
) : // $FlowFixMe[constant-condition]
1139-
isSaving ? (
1136+
) : isSaving ? (
11401137
<LineStackLayout noMargin alignItems="center">
11411138
<Floppy fontSize="small" />
11421139
<Text size="body-small" noMargin color="secondary">
@@ -1173,7 +1170,6 @@ export const ChatMessages: React.ComponentType<Props> = React.memo<Props>(
11731170
</Text>
11741171
</LineStackLayout>
11751172
)}
1176-
{/* $FlowFixMe[constant-condition] */}
11771173
{isRestored && !isForking && forkedFromAiRequestId && (
11781174
<LineStackLayout
11791175
noMargin
@@ -1266,25 +1262,23 @@ export const ChatMessages: React.ComponentType<Props> = React.memo<Props>(
12661262
) : shouldBeWorkingIfNotPaused ? (
12671263
<Line justifyContent="flex-start">
12681264
<div className={classes.thinkingText}>
1269-
{/* $FlowFixMe[constant-condition] */}
1270-
{onPause &&
1271-
(aiRequest.mode === 'agent' ||
1272-
aiRequest.mode === 'orchestrator') && (
1273-
<IconButton
1274-
onClick={() => onPause(!isPaused)}
1275-
size="small"
1276-
style={{
1277-
backgroundColor: !isPaused
1278-
? getBackgroundColor(theme, 'light')
1279-
: undefined,
1280-
borderRadius: 4,
1281-
padding: 0,
1282-
}}
1283-
selected={isPaused}
1284-
>
1285-
{isPaused ? <Play /> : <Pause />}
1286-
</IconButton>
1287-
)}
1265+
{(aiRequest.mode === 'agent' ||
1266+
aiRequest.mode === 'orchestrator') && (
1267+
<IconButton
1268+
onClick={() => onPause(!isPaused)}
1269+
size="small"
1270+
style={{
1271+
backgroundColor: !isPaused
1272+
? getBackgroundColor(theme, 'light')
1273+
: undefined,
1274+
borderRadius: 4,
1275+
padding: 0,
1276+
}}
1277+
selected={isPaused}
1278+
>
1279+
{isPaused ? <Play /> : <Pause />}
1280+
</IconButton>
1281+
)}
12881282
<Spacer />
12891283
<Text
12901284
noMargin

newIDE/app/src/AiGeneration/AiRequestChat/index.js

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,40 @@ const getPriceAndRequestsTextAndTooltip = ({
9696
availableCredits,
9797
selectedMode,
9898
automaticallyUseCreditsForAiRequests,
99+
isRefreshingLimits,
99100
}: {|
100101
quota: Quota | null,
101102
price: UsagePrice | null,
102103
availableCredits: number,
103104
selectedMode: 'chat' | 'agent' | 'orchestrator',
104105
automaticallyUseCreditsForAiRequests: boolean,
106+
isRefreshingLimits?: boolean,
105107
|}): React.Node => {
106108
if (!quota || !price) {
109+
if (isRefreshingLimits) {
110+
// Placeholder to avoid layout shift, while showing the (i) icon.
111+
return (
112+
<Text
113+
size="body-small"
114+
color="secondary"
115+
noMargin
116+
// $FlowFixMe[incompatible-type]
117+
style={{ textAlign: 'right' }}
118+
>
119+
<Trans>Calculating...</Trans>
120+
<span
121+
style={{
122+
verticalAlign: 'middle',
123+
display: 'inline-block',
124+
marginRight: -3,
125+
marginTop: 1,
126+
}}
127+
>
128+
<CircledInfo color="inherit" />
129+
</span>
130+
</Text>
131+
);
132+
}
107133
// Placeholder to avoid layout shift.
108134
return <div style={{ height: 29 }} />;
109135
}
@@ -174,25 +200,38 @@ const getPriceAndRequestsTextAndTooltip = ({
174200
const shouldShowCredits =
175201
quota.limitReached && automaticallyUseCreditsForAiRequests;
176202

203+
const iconSpanStyle = {
204+
verticalAlign: 'middle',
205+
display: 'inline-block',
206+
marginTop: 1,
207+
};
208+
177209
return (
178-
<LineStackLayout alignItems="center" justifyContent="flex-end" noMargin>
179-
{shouldShowCredits && <Coin fontSize="small" />}
180-
<Text size="body-small" color="secondary" noMargin>
181-
{shouldShowCredits ? creditsText : currentQuotaText}
182-
<span
183-
style={{
184-
verticalAlign: 'middle',
185-
display: 'inline-block',
186-
marginRight: -3,
187-
marginTop: 1,
188-
}}
189-
>
190-
<Tooltip title={tooltipText} placement="top" interactive>
191-
<CircledInfo color="inherit" />
192-
</Tooltip>
210+
<Text
211+
size="body-small"
212+
color="secondary"
213+
noMargin
214+
// $FlowFixMe[incompatible-type]
215+
style={{ textAlign: 'right' }}
216+
>
217+
{!isRefreshingLimits && shouldShowCredits && (
218+
<span style={{ ...iconSpanStyle, marginRight: 4 }}>
219+
<Coin fontSize="small" />
193220
</span>
194-
</Text>
195-
</LineStackLayout>
221+
)}
222+
{isRefreshingLimits ? (
223+
<Trans>Calculating...</Trans>
224+
) : shouldShowCredits ? (
225+
creditsText
226+
) : (
227+
currentQuotaText
228+
)}
229+
<span style={{ ...iconSpanStyle, marginRight: -3 }}>
230+
<Tooltip title={tooltipText} placement="top" interactive>
231+
<CircledInfo color="inherit" />
232+
</Tooltip>
233+
</span>
234+
</Text>
196235
);
197236
};
198237

@@ -313,6 +352,7 @@ type Props = {|
313352
increaseQuotaOffering: 'subscribe' | 'upgrade' | 'none',
314353
price: UsagePrice | null,
315354
availableCredits: number,
355+
isRefreshingLimits?: boolean,
316356

317357
standAloneForm?: boolean,
318358

@@ -349,6 +389,7 @@ export const AiRequestChat: React.ComponentType<{
349389
lastSendError,
350390
price,
351391
availableCredits,
392+
isRefreshingLimits,
352393
hasOpenedProject,
353394
editorFunctionCallResults,
354395
onProcessFunctionCalls,
@@ -532,6 +573,7 @@ export const AiRequestChat: React.ComponentType<{
532573
availableCredits,
533574
selectedMode,
534575
automaticallyUseCreditsForAiRequests,
576+
isRefreshingLimits,
535577
});
536578

537579
const chosenOrDefaultAiConfigurationPresetId =

newIDE/app/src/AiGeneration/AskAiEditorContainer.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ export const AskAiEditor: React.ComponentType<Props> = React.memo<Props>(
403403
subscription,
404404
} = authenticatedUser;
405405

406+
const [isRefreshingLimits, setIsRefreshingLimits] = React.useState(false);
407+
406408
const availableCredits = limits ? limits.credits.userBalance.amount : 0;
407409
const quota =
408410
(limits && limits.quotas && limits.quotas['consumed-ai-credits']) ||
@@ -419,7 +421,16 @@ export const AskAiEditor: React.ComponentType<Props> = React.memo<Props>(
419421
React.useEffect(
420422
() => {
421423
if (isActive) {
422-
onRefreshLimits();
424+
(async () => {
425+
setIsRefreshingLimits(true);
426+
try {
427+
await onRefreshLimits();
428+
} catch (error) {
429+
// Ignore limits refresh error.
430+
}
431+
await delay(200);
432+
setIsRefreshingLimits(false);
433+
})();
423434
}
424435
},
425436
[isActive, onRefreshLimits]
@@ -555,11 +566,14 @@ export const AskAiEditor: React.ComponentType<Props> = React.memo<Props>(
555566
// Refresh the user limits, to ensure quota and credits information
556567
// is up-to-date after an AI request.
557568
await delay(500);
569+
setIsRefreshingLimits(true);
558570
try {
559571
await retryIfFailed({ times: 2 }, onRefreshLimits);
560572
} catch (error) {
561573
// Ignore limits refresh error.
562574
}
575+
await delay(200);
576+
setIsRefreshingLimits(false);
563577
})();
564578
},
565579
[
@@ -763,11 +777,14 @@ export const AskAiEditor: React.ComponentType<Props> = React.memo<Props>(
763777
// Refresh the user limits, to ensure quota and credits information
764778
// is up-to-date after an AI request.
765779
await delay(500);
780+
setIsRefreshingLimits(true);
766781
try {
767782
await retryIfFailed({ times: 2 }, onRefreshLimits);
768783
} catch (error) {
769784
// Ignore limits refresh error.
770785
}
786+
await delay(200);
787+
setIsRefreshingLimits(false);
771788

772789
if (
773790
selectedAiRequest &&
@@ -1265,6 +1282,7 @@ export const AskAiEditor: React.ComponentType<Props> = React.memo<Props>(
12651282
}
12661283
price={aiRequestPrice}
12671284
availableCredits={availableCredits}
1285+
isRefreshingLimits={isRefreshingLimits}
12681286
onSendFeedback={onSendFeedback}
12691287
hasOpenedProject={!!project}
12701288
isAutoProcessingFunctionCalls={

newIDE/app/src/AiGeneration/AskAiStandAloneForm.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ export const AskAiStandAloneForm = ({
199199
} = React.useContext(AuthenticatedUserContext);
200200
const { openSubscriptionDialog } = React.useContext(SubscriptionContext);
201201

202+
const [isRefreshingLimits, setIsRefreshingLimits] = React.useState(false);
203+
202204
const hideAskAi =
203205
!!limits &&
204206
!!limits.capabilities.classrooms &&
@@ -217,7 +219,16 @@ export const AskAiStandAloneForm = ({
217219
// we display the proper quota and credits information for the user.
218220
React.useEffect(
219221
() => {
220-
onRefreshLimits();
222+
(async () => {
223+
setIsRefreshingLimits(true);
224+
try {
225+
await onRefreshLimits();
226+
} catch (error) {
227+
// Ignore limits refresh error.
228+
}
229+
await delay(200);
230+
setIsRefreshingLimits(false);
231+
})();
221232
},
222233
// Only on mount, we'll refresh again when sending an AI request.
223234
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -331,11 +342,14 @@ export const AskAiStandAloneForm = ({
331342
// Refresh the user limits, to ensure quota and credits information
332343
// is up-to-date after an AI request.
333344
await delay(500);
345+
setIsRefreshingLimits(true);
334346
try {
335347
await retryIfFailed({ times: 2 }, onRefreshLimits);
336348
} catch (error) {
337349
// Ignore limits refresh error.
338350
}
351+
await delay(200);
352+
setIsRefreshingLimits(false);
339353
})();
340354
},
341355
[
@@ -491,11 +505,14 @@ export const AskAiStandAloneForm = ({
491505
// Refresh the user limits, to ensure quota and credits information
492506
// is up-to-date after an AI request.
493507
await delay(500);
508+
setIsRefreshingLimits(true);
494509
try {
495510
await retryIfFailed({ times: 2 }, onRefreshLimits);
496511
} catch (error) {
497512
// Ignore limits refresh error.
498513
}
514+
await delay(200);
515+
setIsRefreshingLimits(false);
499516
},
500517
[
501518
profile,
@@ -633,6 +650,7 @@ export const AskAiStandAloneForm = ({
633650
}
634651
price={aiRequestPrice}
635652
availableCredits={availableCredits}
653+
isRefreshingLimits={isRefreshingLimits}
636654
onSendFeedback={async () => {}}
637655
hasOpenedProject={!!project}
638656
isAutoProcessingFunctionCalls={

0 commit comments

Comments
 (0)