Skip to content

Commit b2c5062

Browse files
committed
fix(account): use home workspace for sidebar controls
1 parent 00e6dd4 commit b2c5062

4 files changed

Lines changed: 177 additions & 93 deletions

File tree

src/features/app/components/MainApp.tsx

Lines changed: 96 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -717,95 +717,6 @@ export default function MainApp() {
717717
onDebug: addDebugEntry,
718718
});
719719

720-
const {
721-
activeAccount,
722-
accountSwitching,
723-
savedProfiles,
724-
savedProfilesLoading,
725-
activatingProfileId,
726-
handleSwitchAccount,
727-
handleCancelSwitchAccount,
728-
handleActivateSavedProfile,
729-
} = useAccountSwitching({
730-
activeWorkspaceId,
731-
accountByWorkspace,
732-
activeRateLimits,
733-
refreshAccountInfo,
734-
refreshAccountRateLimits,
735-
alertError,
736-
});
737-
const handleResetUsageLimit = useCallback(async () => {
738-
const workspaceId = activeWorkspaceId;
739-
const availableCount = activeRateLimits?.rateLimitResetCredits?.availableCount ?? 0;
740-
if (!workspaceId || availableCount <= 0 || resettingUsageLimit) {
741-
return;
742-
}
743-
744-
const confirmed = window.confirm(
745-
"Use 1 reset credit to reset your Codex usage limit?",
746-
);
747-
if (!confirmed) {
748-
return;
749-
}
750-
751-
const idempotencyKey =
752-
typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
753-
? crypto.randomUUID()
754-
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
755-
756-
setResettingUsageLimit(true);
757-
addDebugEntry({
758-
id: `${Date.now()}-client-rate-limit-reset`,
759-
timestamp: Date.now(),
760-
source: "client",
761-
label: "account/rateLimitResetCredit/consume",
762-
payload: { workspaceId, idempotencyKey },
763-
});
764-
765-
try {
766-
const response = await consumeRateLimitResetCredit(workspaceId, idempotencyKey);
767-
addDebugEntry({
768-
id: `${Date.now()}-server-rate-limit-reset`,
769-
timestamp: Date.now(),
770-
source: "server",
771-
label: "account/rateLimitResetCredit/consume response",
772-
payload: response,
773-
});
774-
const result =
775-
response?.result && typeof response.result === "object"
776-
? response.result
777-
: response;
778-
const outcome = typeof result?.outcome === "string" ? result.outcome : null;
779-
if (outcome === "noCredit") {
780-
alertError("No reset credits are available.");
781-
} else if (outcome === "nothingToReset") {
782-
alertError("There is no active usage limit to reset.");
783-
}
784-
await refreshAccountRateLimits(workspaceId);
785-
} catch (error) {
786-
addDebugEntry({
787-
id: `${Date.now()}-client-rate-limit-reset-error`,
788-
timestamp: Date.now(),
789-
source: "error",
790-
label: "account/rateLimitResetCredit/consume error",
791-
payload: error instanceof Error ? error.message : String(error),
792-
});
793-
alertError(
794-
error instanceof Error
795-
? `Reset failed: ${error.message}`
796-
: "Reset failed.",
797-
);
798-
} finally {
799-
setResettingUsageLimit(false);
800-
}
801-
}, [
802-
activeRateLimits?.rateLimitResetCredits?.availableCount,
803-
activeWorkspaceId,
804-
addDebugEntry,
805-
alertError,
806-
refreshAccountRateLimits,
807-
resettingUsageLimit,
808-
]);
809720
const {
810721
newAgentDraftWorkspaceId,
811722
startingDraftThreadWorkspaceId,
@@ -1146,6 +1057,7 @@ export default function MainApp() {
11461057
});
11471058

11481059
const {
1060+
homeAccountWorkspaceId,
11491061
homeAccount,
11501062
homeRateLimits,
11511063
} = useHomeAccount({
@@ -1159,6 +1071,99 @@ export default function MainApp() {
11591071
refreshAccountInfo,
11601072
refreshAccountRateLimits,
11611073
});
1074+
const accountWorkspaceId = activeWorkspace ? activeWorkspaceId : homeAccountWorkspaceId;
1075+
const accountWorkspaceRateLimits = activeWorkspace ? activeRateLimits : homeRateLimits;
1076+
const {
1077+
activeAccount,
1078+
accountSwitching,
1079+
savedProfiles,
1080+
savedProfilesLoading,
1081+
activatingProfileId,
1082+
handleSwitchAccount,
1083+
handleCancelSwitchAccount,
1084+
handleActivateSavedProfile,
1085+
} = useAccountSwitching({
1086+
activeWorkspaceId: accountWorkspaceId,
1087+
accountByWorkspace,
1088+
activeRateLimits: accountWorkspaceRateLimits,
1089+
refreshAccountInfo,
1090+
refreshAccountRateLimits,
1091+
alertError,
1092+
});
1093+
const resetUsageWorkspaceId = activeWorkspace ? activeWorkspaceId : homeAccountWorkspaceId;
1094+
const resetUsageRateLimits = activeWorkspace ? activeRateLimits : homeRateLimits;
1095+
const handleResetUsageLimit = useCallback(async () => {
1096+
const workspaceId = resetUsageWorkspaceId;
1097+
const availableCount = resetUsageRateLimits?.rateLimitResetCredits?.availableCount ?? 0;
1098+
if (!workspaceId || availableCount <= 0 || resettingUsageLimit) {
1099+
return;
1100+
}
1101+
1102+
const confirmed = window.confirm(
1103+
"Use 1 reset credit to reset your Codex usage limit?",
1104+
);
1105+
if (!confirmed) {
1106+
return;
1107+
}
1108+
1109+
const idempotencyKey =
1110+
typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
1111+
? crypto.randomUUID()
1112+
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
1113+
1114+
setResettingUsageLimit(true);
1115+
addDebugEntry({
1116+
id: `${Date.now()}-client-rate-limit-reset`,
1117+
timestamp: Date.now(),
1118+
source: "client",
1119+
label: "account/rateLimitResetCredit/consume",
1120+
payload: { workspaceId, idempotencyKey },
1121+
});
1122+
1123+
try {
1124+
const response = await consumeRateLimitResetCredit(workspaceId, idempotencyKey);
1125+
addDebugEntry({
1126+
id: `${Date.now()}-server-rate-limit-reset`,
1127+
timestamp: Date.now(),
1128+
source: "server",
1129+
label: "account/rateLimitResetCredit/consume response",
1130+
payload: response,
1131+
});
1132+
const result =
1133+
response?.result && typeof response.result === "object"
1134+
? response.result
1135+
: response;
1136+
const outcome = typeof result?.outcome === "string" ? result.outcome : null;
1137+
if (outcome === "noCredit") {
1138+
alertError("No reset credits are available.");
1139+
} else if (outcome === "nothingToReset") {
1140+
alertError("There is no active usage limit to reset.");
1141+
}
1142+
await refreshAccountRateLimits(workspaceId);
1143+
} catch (error) {
1144+
addDebugEntry({
1145+
id: `${Date.now()}-client-rate-limit-reset-error`,
1146+
timestamp: Date.now(),
1147+
source: "error",
1148+
label: "account/rateLimitResetCredit/consume error",
1149+
payload: error instanceof Error ? error.message : String(error),
1150+
});
1151+
alertError(
1152+
error instanceof Error
1153+
? `Reset failed: ${error.message}`
1154+
: "Reset failed.",
1155+
);
1156+
} finally {
1157+
setResettingUsageLimit(false);
1158+
}
1159+
}, [
1160+
addDebugEntry,
1161+
alertError,
1162+
refreshAccountRateLimits,
1163+
resetUsageRateLimits?.rateLimitResetCredits?.availableCount,
1164+
resetUsageWorkspaceId,
1165+
resettingUsageLimit,
1166+
]);
11621167
const activeTokenUsage = activeThreadId
11631168
? tokenUsageByThread[activeThreadId] ?? null
11641169
: null;
@@ -1697,6 +1702,8 @@ export default function MainApp() {
16971702
approvals,
16981703
activeRateLimits,
16991704
activeAccount,
1705+
accountWorkspaceId,
1706+
homeAccountWorkspaceId,
17001707
homeRateLimits,
17011708
homeAccount,
17021709
accountSwitching,

src/features/app/components/Sidebar.test.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const baseProps = {
3232
activeWorkspaceId: null,
3333
activeThreadId: null,
3434
accountRateLimits: null,
35+
accountWorkspaceId: null,
36+
resetUsageWorkspaceId: null,
3537
usageShowRemaining: false,
3638
accountInfo: null,
3739
savedProfiles: [],
@@ -166,6 +168,8 @@ describe("Sidebar", () => {
166168
<Sidebar
167169
{...baseProps}
168170
activeWorkspaceId="ws-1"
171+
accountWorkspaceId="ws-1"
172+
resetUsageWorkspaceId="ws-1"
169173
onResetUsageLimit={onResetUsageLimit}
170174
accountRateLimits={{
171175
primary: {
@@ -192,11 +196,45 @@ describe("Sidebar", () => {
192196
expect(onResetUsageLimit).toHaveBeenCalledTimes(1);
193197
});
194198

199+
it("enables reset credits for the Home account workspace before a chat is active", () => {
200+
const onResetUsageLimit = vi.fn();
201+
render(
202+
<Sidebar
203+
{...baseProps}
204+
activeWorkspaceId={null}
205+
accountWorkspaceId="ws-1"
206+
resetUsageWorkspaceId="ws-1"
207+
onResetUsageLimit={onResetUsageLimit}
208+
accountRateLimits={{
209+
primary: {
210+
usedPercent: 100,
211+
windowDurationMins: 300,
212+
resetsAt: Math.round(Date.now() / 1000) + 3600,
213+
},
214+
secondary: null,
215+
credits: null,
216+
rateLimitResetCredits: {
217+
availableCount: 2,
218+
},
219+
planType: "pro",
220+
}}
221+
/>,
222+
);
223+
224+
const resetButton = screen.getByRole("button", { name: "Reset usage limit" });
225+
expect((resetButton as HTMLButtonElement).disabled).toBe(false);
226+
227+
fireEvent.click(resetButton);
228+
229+
expect(onResetUsageLimit).toHaveBeenCalledTimes(1);
230+
});
231+
195232
it("opens the account menu from the bottom rail", () => {
196233
render(
197234
<Sidebar
198235
{...baseProps}
199236
activeWorkspaceId="ws-1"
237+
accountWorkspaceId="ws-1"
200238
accountInfo={{
201239
email: "dimillian@example.com",
202240
type: "chatgpt",
@@ -212,6 +250,27 @@ describe("Sidebar", () => {
212250
expect(screen.getByRole("button", { name: "Switch account" })).toBeTruthy();
213251
});
214252

253+
it("shows the account menu for the Home account workspace before a chat is active", () => {
254+
render(
255+
<Sidebar
256+
{...baseProps}
257+
activeWorkspaceId={null}
258+
accountWorkspaceId="ws-1"
259+
accountInfo={{
260+
email: "home@example.com",
261+
type: "chatgpt",
262+
planType: "plus",
263+
requiresOpenaiAuth: false,
264+
}}
265+
/>,
266+
);
267+
268+
fireEvent.click(screen.getByRole("button", { name: "Account" }));
269+
270+
expect(screen.getByText("home@example.com")).toBeTruthy();
271+
expect(screen.getByRole("button", { name: "Switch account" })).toBeTruthy();
272+
});
273+
215274
it("renders threads-only mode as a global chronological list", () => {
216275
const older = Date.now() - 10_000;
217276
const newer = Date.now();

src/features/app/components/Sidebar.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ type SidebarProps = {
120120
activeThreadId: string | null;
121121
userInputRequests?: RequestUserInputRequest[];
122122
accountRateLimits: RateLimitSnapshot | null;
123+
accountWorkspaceId: string | null;
124+
resetUsageWorkspaceId: string | null;
123125
usageShowRemaining: boolean;
124126
accountInfo: AccountSnapshot | null;
125127
savedProfiles: SavedAccountProfile[];
@@ -187,6 +189,8 @@ export const Sidebar = memo(function Sidebar({
187189
activeThreadId,
188190
userInputRequests = [],
189191
accountRateLimits,
192+
accountWorkspaceId,
193+
resetUsageWorkspaceId,
190194
usageShowRemaining,
191195
accountInfo,
192196
savedProfiles,
@@ -375,9 +379,9 @@ export const Sidebar = memo(function Sidebar({
375379
? "API key"
376380
: "Sign in to Codex";
377381
const accountActionLabel = accountEmail ? "Switch account" : "Sign in";
378-
const showAccountSwitcher = Boolean(activeWorkspaceId);
379-
const accountSwitchDisabled = accountSwitching || !activeWorkspaceId;
380-
const accountCancelDisabled = !accountSwitching || !activeWorkspaceId;
382+
const showAccountSwitcher = Boolean(accountWorkspaceId);
383+
const accountSwitchDisabled = accountSwitching || !accountWorkspaceId;
384+
const accountCancelDisabled = !accountSwitching || !accountWorkspaceId;
381385
const refreshDisabled = workspaces.length === 0 || workspaces.every((workspace) => !workspace.connected);
382386
const refreshInProgress = workspaces.some(
383387
(workspace) => threadListLoadingByWorkspace[workspace.id] ?? false,
@@ -1052,7 +1056,7 @@ export const Sidebar = memo(function Sidebar({
10521056
onResetUsageLimit={onResetUsageLimit}
10531057
resetUsageDisabled={
10541058
resettingUsageLimit ||
1055-
!activeWorkspaceId ||
1059+
!resetUsageWorkspaceId ||
10561060
!accountRateLimits?.rateLimitResetCredits?.availableCount
10571061
}
10581062
resettingUsageLimit={resettingUsageLimit}

0 commit comments

Comments
 (0)