Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { beforeEach, describe, expect, it, vi } from "vitest";

const { getItem, setItem, removeItem } = vi.hoisted(() => ({
getItem: vi.fn(),
setItem: vi.fn(),
removeItem: vi.fn(),
}));

vi.mock("@renderer/trpc/client", () => ({
trpcClient: {
secureStore: {
getItem: { query: getItem },
setItem: { query: setItem },
removeItem: { query: removeItem },
},
},
}));

import { useSettingsStore } from "./settingsStore";

describe("feature settingsStore cloud selections", () => {
beforeEach(() => {
getItem.mockReset();
setItem.mockReset();
removeItem.mockReset();
getItem.mockResolvedValue(null);
setItem.mockResolvedValue(undefined);
removeItem.mockResolvedValue(undefined);

useSettingsStore.setState({
lastUsedCloudRepository: null,
});
});

it("persists the last used cloud repository", async () => {
useSettingsStore.getState().setLastUsedCloudRepository("posthog/posthog");

await vi.waitFor(() => {
expect(setItem).toHaveBeenCalled();
});

const lastCall = setItem.mock.calls[setItem.mock.calls.length - 1];
const persisted = JSON.parse(lastCall[0].value);

expect(persisted.state.lastUsedCloudRepository).toBe("posthog/posthog");
});

it("rehydrates the last used cloud repository", async () => {
getItem.mockResolvedValue(
JSON.stringify({
state: {
lastUsedCloudRepository: "posthog/posthog",
},
version: 0,
}),
);

useSettingsStore.setState({
lastUsedCloudRepository: null,
});

await useSettingsStore.persist.rehydrate();

expect(useSettingsStore.getState().lastUsedCloudRepository).toBe(
"posthog/posthog",
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface SettingsStore {
lastUsedWorkspaceMode: WorkspaceMode;
lastUsedAdapter: AgentAdapter;
lastUsedModel: string | null;
lastUsedCloudRepository: string | null;
lastUsedEnvironments: Record<string, string>;
desktopNotifications: boolean;
dockBadgeNotifications: boolean;
Expand Down Expand Up @@ -57,6 +58,7 @@ interface SettingsStore {
setLastUsedWorkspaceMode: (mode: WorkspaceMode) => void;
setLastUsedAdapter: (adapter: AgentAdapter) => void;
setLastUsedModel: (model: string) => void;
setLastUsedCloudRepository: (repo: string | null) => void;
setLastUsedEnvironment: (
repoPath: string,
environmentId: string | null,
Expand Down Expand Up @@ -88,6 +90,7 @@ export const useSettingsStore = create<SettingsStore>()(
lastUsedWorkspaceMode: "local",
lastUsedAdapter: "claude",
lastUsedModel: null,
lastUsedCloudRepository: null,
lastUsedEnvironments: {},
desktopNotifications: true,
dockBadgeNotifications: true,
Expand Down Expand Up @@ -143,6 +146,8 @@ export const useSettingsStore = create<SettingsStore>()(
setLastUsedWorkspaceMode: (mode) => set({ lastUsedWorkspaceMode: mode }),
setLastUsedAdapter: (adapter) => set({ lastUsedAdapter: adapter }),
setLastUsedModel: (model) => set({ lastUsedModel: model }),
setLastUsedCloudRepository: (repo) =>
set({ lastUsedCloudRepository: repo }),
setLastUsedEnvironment: (repoPath, environmentId) =>
set((state) => {
const next = { ...state.lastUsedEnvironments };
Expand Down Expand Up @@ -190,6 +195,7 @@ export const useSettingsStore = create<SettingsStore>()(
lastUsedWorkspaceMode: state.lastUsedWorkspaceMode,
lastUsedAdapter: state.lastUsedAdapter,
lastUsedModel: state.lastUsedModel,
lastUsedCloudRepository: state.lastUsedCloudRepository,
lastUsedEnvironments: state.lastUsedEnvironments,
desktopNotifications: state.desktopNotifications,
dockBadgeNotifications: state.dockBadgeNotifications,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { useAuthStore } from "@renderer/features/auth/stores/authStore";
import { useTRPC } from "@renderer/trpc/client";
import { useNavigationStore } from "@stores/navigationStore";
import { useQuery } from "@tanstack/react-query";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { usePreviewConfig } from "../hooks/usePreviewConfig";
import { useTaskCreation } from "../hooks/useTaskCreation";
Expand Down Expand Up @@ -59,6 +59,8 @@ export function TaskInput({
setLastUsedWorkspaceMode,
lastUsedAdapter,
setLastUsedAdapter,
lastUsedCloudRepository,
setLastUsedCloudRepository,
allowBypassPermissions,
setLastUsedEnvironment,
getLastUsedEnvironment,
Expand Down Expand Up @@ -103,13 +105,18 @@ export function TaskInput({
const { githubIntegration, repositories, isLoadingRepos } =
useRepositoryIntegration();
const [selectedRepository, setSelectedRepository] = useState<string | null>(
null,
() => lastUsedCloudRepository?.toLowerCase() ?? null,
);
const selectedCloudRepository = useMemo(() => {
if (!selectedRepository) return null;
const lower = selectedRepository.toLowerCase();
return repositories.includes(lower) ? lower : null;
}, [selectedRepository, repositories]);
const { currentBranch, branchLoading, defaultBranch } =
useGitQueries(selectedDirectory);

const { data: cloudBranchData, isPending: cloudBranchesLoading } =
useGithubBranches(githubIntegration?.id, selectedRepository);
useGithubBranches(githubIntegration?.id, selectedCloudRepository);
const cloudBranches = cloudBranchData?.branches;
const cloudDefaultBranch = cloudBranchData?.defaultBranch ?? null;

Expand Down Expand Up @@ -149,6 +156,15 @@ export function TaskInput({
}
}, [selectedDirectory, newBranchName, gitActions]);

const handleRepositorySelect = useCallback(
(repo: string) => {
const normalizedRepo = repo.toLowerCase();
setSelectedRepository(normalizedRepo);
setLastUsedCloudRepository(normalizedRepo);
},
[setLastUsedCloudRepository],
);

const {
modeOption,
modelOption,
Expand All @@ -159,6 +175,37 @@ export function TaskInput({

const { folders } = useFolders();

useEffect(() => {
if (selectedRepository || !lastUsedCloudRepository) {
return;
}

setSelectedRepository(lastUsedCloudRepository.toLowerCase());
}, [lastUsedCloudRepository, selectedRepository]);

useEffect(() => {
if (
isLoadingRepos ||
!githubIntegration ||
!selectedRepository ||
selectedCloudRepository
) {
return;
}

setSelectedRepository(null);
if (lastUsedCloudRepository === selectedRepository) {
setLastUsedCloudRepository(null);
}
}, [
githubIntegration,
isLoadingRepos,
lastUsedCloudRepository,
selectedCloudRepository,
selectedRepository,
setLastUsedCloudRepository,
]);

useEffect(() => {
if (view.folderId) {
const folder = folders.find((f) => f.id === view.folderId);
Expand All @@ -169,7 +216,7 @@ export function TaskInput({
}, [view.folderId, folders]);

const effectiveRepoPath =
workspaceMode === "cloud" ? selectedRepository : selectedDirectory;
workspaceMode === "cloud" ? selectedCloudRepository : selectedDirectory;

const setSelectedEnvironment = useCallback(
(envId: string | null) => {
Expand All @@ -183,6 +230,7 @@ export function TaskInput({

useEffect(() => {
setSelectedBranch(null);

if (effectiveRepoPath) {
setSelectedEnvironmentRaw(getLastUsedEnvironment(effectiveRepoPath));
} else {
Expand Down Expand Up @@ -212,7 +260,7 @@ export function TaskInput({
const { isCreatingTask, canSubmit, handleSubmit } = useTaskCreation({
editorRef,
selectedDirectory,
selectedRepository,
selectedRepository: selectedCloudRepository,
githubIntegrationId: githubIntegration?.id,
workspaceMode: effectiveWorkspaceMode,
branch: branchForTaskCreation,
Expand Down Expand Up @@ -373,7 +421,7 @@ export function TaskInput({
{workspaceMode === "cloud" ? (
<GitHubRepoPicker
value={selectedRepository}
onChange={setSelectedRepository}
onChange={handleRepositorySelect}
repositories={repositories}
isLoading={isLoadingRepos}
placeholder="Select repository..."
Expand All @@ -398,7 +446,7 @@ export function TaskInput({
<BranchSelector
repoPath={
workspaceMode === "cloud"
? selectedRepository
? selectedCloudRepository
: selectedDirectory
}
currentBranch={currentBranch}
Expand All @@ -407,7 +455,7 @@ export function TaskInput({
}
disabled={
isCreatingTask ||
(workspaceMode === "cloud" && !selectedRepository)
(workspaceMode === "cloud" && !selectedCloudRepository)
}
loading={branchLoading}
workspaceMode={workspaceMode}
Expand Down Expand Up @@ -446,7 +494,7 @@ export function TaskInput({
onSubmit={handleSubmit}
hasDirectory={
workspaceMode === "cloud"
? !!selectedRepository
? !!selectedCloudRepository
: !!selectedDirectory
}
directoryTooltip={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ export function useTaskCreation({
const { invalidateTasks } = useCreateTask();
const { isOnline } = useConnectivity();

// Cloud mode can work with either selectedRepository (production) or selectedDirectory (dev testing)
const hasRequiredPath = !!selectedRepository || !!selectedDirectory;
const hasRequiredPath =
workspaceMode === "cloud" ? !!selectedRepository : !!selectedDirectory;
const canSubmit =
!!editorRef.current &&
isAuthenticated &&
Expand Down
Loading
Loading