fix(desktop): build Account view links from the sidecar's Cline app and the loaded user

This commit is contained in:
Saoud Rizwan
2026-10-21 00:50:19 +00:00
parent 9165126e71
commit 2d5d40fda6
3 changed files with 38 additions and 13 deletions
@@ -24,7 +24,7 @@ beforeEach(() => {
invoke.mockReset();
openExternalUrl.mockReset();
useAccount.mockReturnValue({
privacySettingsUrl: undefined,
clineAppBaseUrl: undefined,
refreshAccount: vi.fn(),
});
container = document.createElement("div");
@@ -243,8 +243,11 @@ describe("AccountView signed-out state", () => {
});
describe("AccountView privacy settings", () => {
async function renderSignedInWithPrivacyLink(privacySettingsUrl?: string) {
useAccount.mockReturnValue({ privacySettingsUrl, refreshAccount: vi.fn() });
async function renderSignedInWithPrivacyLink(dataPrivacyPath?: string) {
useAccount.mockReturnValue({
clineAppBaseUrl: "https://staging-app.cline.bot",
refreshAccount: vi.fn(),
});
invoke.mockImplementation(
async (_command: string, args?: Record<string, unknown>) => {
switch (args?.operation) {
@@ -256,6 +259,7 @@ describe("AccountView privacy settings", () => {
createdAt: "2024-01-01T00:00:00Z",
updatedAt: "2024-01-01T00:00:00Z",
organizations: [],
dataPrivacyPath,
};
case "fetchBalance":
return { balance: 5_000_000 };
@@ -274,6 +278,11 @@ describe("AccountView privacy settings", () => {
});
}
const dashboardButton = () =>
container.querySelector<HTMLButtonElement>(
'button[title="Open dashboard"]',
);
const privacySettingsButton = () =>
container.querySelector<HTMLButtonElement>(
'button[title="Privacy settings"]',
@@ -282,21 +291,23 @@ describe("AccountView privacy settings", () => {
it("hides the action when there is no privacy settings link", async () => {
await renderSignedInWithPrivacyLink(undefined);
expect(
container.querySelector('button[title="Open dashboard"]'),
).not.toBeNull();
expect(dashboardButton()).not.toBeNull();
expect(privacySettingsButton()).toBeNull();
});
it("opens the privacy settings link from the account context", async () => {
await renderSignedInWithPrivacyLink(
"https://staging-app.cline.bot/dashboard/account?tab=privacy&client=desktop",
);
it("opens privacy settings and the dashboard on the sidecar's Cline app", async () => {
await renderSignedInWithPrivacyLink("/dashboard/account?tab=privacy");
await act(async () => privacySettingsButton()?.click());
await act(async () => dashboardButton()?.click());
expect(openExternalUrl).toHaveBeenCalledWith(
expect(openExternalUrl).toHaveBeenNthCalledWith(
1,
"https://staging-app.cline.bot/dashboard/account?tab=privacy&client=desktop",
);
expect(openExternalUrl).toHaveBeenNthCalledWith(
2,
"https://staging-app.cline.bot/dashboard",
);
});
});
@@ -9,6 +9,7 @@ import type {
ClineAccountUsageTransaction,
ClineAccountUser,
} from "@cline/core";
import { getClinePrivacySettingsUrl } from "@cline/shared/browser";
import {
AlertCircle,
Building,
@@ -165,10 +166,20 @@ export function AccountView() {
const [activeTab, setActiveTab] = useState<"overview" | "usage" | "billing">(
"overview",
);
const { privacySettingsUrl, refreshAccount } = useAccount();
const { clineAppBaseUrl, refreshAccount } = useAccount();
// Overview data
const [user, setUser] = useState<ClineAccountUser | null>(null);
const dashboardUrl = clineAppBaseUrl
? `${clineAppBaseUrl}/dashboard`
: DASHBOARD_URL;
const privacySettingsUrl = clineAppBaseUrl
? getClinePrivacySettingsUrl(
user?.dataPrivacyPath,
clineAppBaseUrl,
"desktop",
)
: undefined;
const [balance, setBalance] = useState<ClineAccountBalance | null>(null);
const [organizationBalance, setOrganizationBalance] =
useState<ClineAccountOrganizationBalance | null>(null);
@@ -663,7 +674,7 @@ export function AccountView() {
<button
type="button"
title="Open dashboard"
onClick={() => void openExternalUrl(DASHBOARD_URL)}
onClick={() => void openExternalUrl(dashboardUrl)}
className="rounded-md p-1.5 text-muted-foreground hover:bg-surface-hover hover:text-foreground"
>
<ExternalLink className="h-4 w-4" />
@@ -27,6 +27,7 @@ type AccountContextValue = {
accountReady: boolean;
organizations: ClineAccountOrganization[];
activeOrganization: ClineAccountOrganization | null;
clineAppBaseUrl: string | undefined;
privacySettingsUrl: string | undefined;
refreshAccount: () => Promise<void>;
};
@@ -36,6 +37,7 @@ const AccountContext = createContext<AccountContextValue>({
accountReady: false,
organizations: [],
activeOrganization: null,
clineAppBaseUrl: undefined,
privacySettingsUrl: undefined,
refreshAccount: async () => undefined,
});
@@ -161,6 +163,7 @@ export function AccountProvider({ children }: { children: React.ReactNode }) {
organizations,
activeOrganization:
organizations.find((organization) => organization.active) ?? null,
clineAppBaseUrl,
privacySettingsUrl: clineAppBaseUrl
? getClinePrivacySettingsUrl(
user?.dataPrivacyPath,