Skip to content

Commit 335af3f

Browse files
authored
Coins fix (#898)
* wip(coins): use coin balance from basic stuff query, * wip: balance low info callout * refactor: remove old coin balance slice. * refactor(login): remove old getUser service. * refactor(balance callout): change 0 to low if balance is nullish
1 parent 51a2336 commit 335af3f

File tree

11 files changed

+77
-128
lines changed

11 files changed

+77
-128
lines changed

refact-agent/gui/src/app/middleware.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { pathApi } from "../services/refact/path";
1414
import { pingApi } from "../services/refact/ping";
1515
import {
1616
clearError,
17+
setBallanceError,
1718
setError,
1819
setIsAuthError,
1920
} from "../features/Errors/errorsSlice";
@@ -41,6 +42,7 @@ import {
4142
} from "../services/graphql";
4243
import { push } from "../features/Pages/pagesSlice";
4344
import { setExpert, setModel } from "../features/ExpertsAndModels/expertsSlice";
45+
import { setBallanceInformation } from "../features/Errors/informationSlice";
4446

4547
const AUTH_ERROR_MESSAGE =
4648
"There is an issue with your API key. Check out your API Key or re-login";
@@ -489,3 +491,27 @@ function getModel(preferences: unknown): string | null {
489491
}
490492
return preferences.model;
491493
}
494+
495+
startListening({
496+
matcher: graphqlQueriesAndMutations.endpoints.getBasicStuff.matchFulfilled,
497+
effect: (action, listenerApi) => {
498+
const state = listenerApi.getState();
499+
const currentWorkspace = state.teams.workspace;
500+
if (!currentWorkspace) return;
501+
502+
const workspaceInfo = action.payload.query_basic_stuff.workspaces.find(
503+
(ws) => ws.ws_id === currentWorkspace.ws_id,
504+
);
505+
if (!workspaceInfo) return;
506+
507+
if (!workspaceInfo.have_coins_enough) {
508+
// dispatch global error about not having enough coins
509+
listenerApi.dispatch(setBallanceError("Your balance is exhausted!"));
510+
} else if (
511+
workspaceInfo.have_coins_exactly <= 2000 &&
512+
!state.information.dismissed
513+
) {
514+
listenerApi.dispatch(setBallanceInformation());
515+
}
516+
},
517+
});

refact-agent/gui/src/app/store.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,8 @@ import { currentProjectInfoReducer } from "../features/Chat/currentProject";
4545
import { checkpointsSlice } from "../features/Checkpoints/checkpointsSlice";
4646
import { checkpointsApi } from "../services/refact/checkpoints";
4747
import { patchesAndDiffsTrackerSlice } from "../features/PatchesAndDiffsTracker/patchesAndDiffsTrackerSlice";
48-
import { coinBallanceSlice } from "../features/CoinBalance/coinBalanceSlice";
4948
import { threadListSlice } from "../features/ThreadList/threadListSlice";
5049
import { threadMessagesSlice } from "../features/ThreadMessages/threadMessagesSlice";
51-
// import { expertsAndModelsMiddleWare } from "../features/ExpertsAndModels/middleware";
5250
import { expertsSlice } from "../features/ExpertsAndModels/expertsSlice";
5351
import { graphqlQueriesAndMutations } from "../services/graphql/queriesAndMutationsApi";
5452
import { groupsSlice } from "../features/Groups/groupsSlice";
@@ -103,7 +101,6 @@ const rootReducer = combineSlices(
103101
integrationsSlice,
104102
checkpointsSlice,
105103
patchesAndDiffsTrackerSlice,
106-
coinBallanceSlice,
107104
threadListSlice,
108105
threadMessagesSlice,
109106
expertsSlice,
@@ -193,25 +190,9 @@ export const store = setUpStore();
193190
export type Store = typeof store;
194191

195192
export const persistor = persistStore(store);
196-
// TODO: sync storage across windows (was buggy when deleting).
197-
// window.onstorage = (event) => {
198-
// if (!event.key || !event.key.endsWith(persistConfig.key)) {
199-
// return;
200-
// }
201-
202-
// if (event.oldValue === event.newValue) {
203-
// return;
204-
// }
205-
// if (event.newValue === null) {
206-
// return;
207-
// }
208-
209-
// Infer the `RootState` and `AppDispatch` types from the store itself
210-
// export type RootState = ReturnType<typeof store.getState>;
211-
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
193+
212194
export type AppDispatch = typeof store.dispatch;
213195

214-
// Infer the type of `store`
215196
export type AppStore = typeof store;
216197

217198
declare global {

refact-agent/gui/src/components/Callout/Callout.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ import classNames from "classnames";
1818
import {
1919
useAppDispatch,
2020
useAppSelector,
21+
useCoinBallance,
2122
useConfig,
2223
useLogout,
2324
useOpenUrl,
2425
} from "../../hooks";
2526
import { getIsAuthError } from "../../features/Errors/errorsSlice";
26-
import { selectBalance } from "../../features/CoinBalance";
2727
import { dismissBalanceLowCallout } from "../../features/Errors/informationSlice";
2828

2929
type RadixCalloutProps = React.ComponentProps<typeof RadixCallout.Root>;
@@ -272,7 +272,7 @@ export const BallanceCallOut: React.FC<
272272
export const BallanceLowInformation: React.FC<Omit<CalloutProps, "type">> = (
273273
props,
274274
) => {
275-
const balance = useAppSelector(selectBalance);
275+
const ballance = useCoinBallance();
276276
const dispatch = useAppDispatch();
277277
const handleClose = useCallback(() => {
278278
dispatch(dismissBalanceLowCallout());
@@ -298,7 +298,10 @@ export const BallanceLowInformation: React.FC<Omit<CalloutProps, "type">> = (
298298
onClick={handleClose}
299299
{...props}
300300
>
301-
💸 <Strong>Your balance is {balance}</Strong>
301+
💸{" "}
302+
<Strong>
303+
Your balance is {ballance?.have_coins_exactly ?? "running low"}
304+
</Strong>
302305
<br />
303306
Please{" "}
304307
<Link

refact-agent/gui/src/components/Toolbar/Dropdown.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
// useStartPollingForUser,
99
useEventsBusForIDE,
1010
useBasicStuffQuery,
11+
useCoinBallance,
1112
} from "../../hooks";
1213
import { useOpenUrl } from "../../hooks/useOpenUrl";
1314
import {
@@ -88,6 +89,9 @@ export const Dropdown: React.FC<DropdownProps> = ({
8889
const user = useBasicStuffQuery();
8990
const host = useAppSelector(selectHost);
9091
const dispatch = useAppDispatch();
92+
const ballance = useCoinBallance();
93+
94+
const coinBallance = ballance?.have_coins_exactly ?? 0;
9195
// TODO: check how much of this is still used.
9296
// const { maxAgentUsageAmount, currentAgentUsage } = useAgentUsage();
9397

@@ -97,17 +101,6 @@ export const Dropdown: React.FC<DropdownProps> = ({
97101
const activeWorkspace = useAppSelector(selectActiveWorkspace);
98102
const activeGroup = useAppSelector(selectActiveGroup);
99103

100-
const coinBalance = useMemo(() => {
101-
const maybeWorkspaceWithCoins =
102-
user.data?.query_basic_stuff.workspaces.find(
103-
(w) => w.ws_id === activeWorkspace?.ws_id,
104-
);
105-
if (!maybeWorkspaceWithCoins) return null;
106-
if (!maybeWorkspaceWithCoins.have_admin) return null;
107-
if (maybeWorkspaceWithCoins.have_coins_exactly === 0) return null;
108-
return Math.round(maybeWorkspaceWithCoins.have_coins_exactly / 1000);
109-
}, [user.data, activeWorkspace?.ws_id]);
110-
111104
const isActiveRootGroup = useMemo(() => {
112105
if (!activeWorkspace || !activeGroup) return false;
113106
return activeWorkspace.root_group_name === activeGroup.name;
@@ -197,11 +190,11 @@ export const Dropdown: React.FC<DropdownProps> = ({
197190
</DropdownMenu.Item>
198191
)}
199192

200-
{user.data && activeWorkspace && coinBalance && (
193+
{user.data && activeWorkspace && coinBallance && (
201194
<DropdownMenu.Label>
202195
<Flex align="center" gap="1">
203196
{/**TODO: there could be multiple source for this */}
204-
{coinBalance} <Coin />
197+
{coinBallance / 100000} <Coin />
205198
<HoverCard.Root>
206199
<HoverCard.Trigger>
207200
<QuestionMarkCircledIcon style={{ marginLeft: 4 }} />

refact-agent/gui/src/features/CoinBalance/coinBalanceSlice.ts

Lines changed: 0 additions & 39 deletions
This file was deleted.

refact-agent/gui/src/features/CoinBalance/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

refact-agent/gui/src/features/Errors/errorsSlice.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export const errorSlice = createSlice({
2828
state.message = null;
2929
state.type = null;
3030
},
31+
setBallanceError: (state, action: PayloadAction<string>) => {
32+
state.type = "balance";
33+
state.message = action.payload;
34+
},
3135
},
3236
selectors: {
3337
getErrorMessage: (state) => state.message,
@@ -36,7 +40,8 @@ export const errorSlice = createSlice({
3640
},
3741
});
3842

39-
export const { setError, setIsAuthError, clearError } = errorSlice.actions;
43+
export const { setError, setIsAuthError, clearError, setBallanceError } =
44+
errorSlice.actions;
4045
export const { getErrorMessage, getIsAuthError, getErrorType } =
4146
errorSlice.selectors;
4247

refact-agent/gui/src/features/Errors/informationSlice.ts

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createSlice, type PayloadAction } from "@reduxjs/toolkit";
2-
import { smallCloudApi } from "../../services/smallcloud";
32
import { threadMessagesSlice } from "../ThreadMessages";
43
import { isUsage } from "../../services/refact/chat";
54

@@ -31,6 +30,10 @@ export const informationSlice = createSlice({
3130
state.type = null;
3231
state.message = null;
3332
},
33+
setBallanceInformation: (state) => {
34+
if (state.dismissed) return state;
35+
state.type = "balance";
36+
},
3437
},
3538
selectors: {
3639
getInformationMessage: (state) => state.message,
@@ -66,24 +69,14 @@ export const informationSlice = createSlice({
6669
return state;
6770
},
6871
);
69-
70-
builder.addMatcher(
71-
smallCloudApi.endpoints.getUser.matchFulfilled,
72-
(state, action) => {
73-
if (state.dismissed) return state;
74-
if (state.message) return state;
75-
if (action.payload.metering_balance <= 2000) {
76-
state.type = "balance";
77-
state.message =
78-
"Your account is running low on credits. Please top up your account to continue using the service.";
79-
}
80-
return state;
81-
},
82-
);
8372
},
8473
});
8574

86-
export const { setInformation, clearInformation, dismissBalanceLowCallout } =
87-
informationSlice.actions;
75+
export const {
76+
setInformation,
77+
clearInformation,
78+
dismissBalanceLowCallout,
79+
setBallanceInformation,
80+
} = informationSlice.actions;
8881
export const { getInformationMessage, showBalanceLowCallout } =
8982
informationSlice.selectors;

refact-agent/gui/src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ export * from "./useIdForThread";
3434
export * from "./useToolsForGroup";
3535
export * from "./useAttachImages";
3636
export * from "./useCapabilitiesForModel";
37+
export * from "./useCoinBalance";
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
1+
import { useMemo } from "react";
2+
import { selectActiveWorkspace } from "../features/Teams/teamsSlice";
13
import { useAppSelector } from "./useAppSelector";
2-
import { selectBalance } from "../features/CoinBalance/coinBalanceSlice";
4+
import { useBasicStuffQuery } from "./useBasicStuffQuery";
35

46
export function useCoinBallance() {
5-
return useAppSelector(selectBalance);
7+
const user = useBasicStuffQuery();
8+
9+
const activeWorkspace = useAppSelector(selectActiveWorkspace);
10+
const balance = useMemo(() => {
11+
const maybeWorkspaceWithCoins =
12+
user.data?.query_basic_stuff.workspaces.find(
13+
(w) => w.ws_id === activeWorkspace?.ws_id,
14+
);
15+
16+
if (!maybeWorkspaceWithCoins) return null;
17+
18+
return {
19+
have_coins_enough: maybeWorkspaceWithCoins.have_coins_enough,
20+
have_coins_exactly: maybeWorkspaceWithCoins.have_coins_exactly as number,
21+
};
22+
}, [user.data, activeWorkspace?.ws_id]);
23+
24+
return balance;
625
}

0 commit comments

Comments
 (0)