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
75 changes: 4 additions & 71 deletions apps/array/src/renderer/features/auth/components/AuthScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { AsciiArt } from "@components/AsciiArt";
import { useAuthStore } from "@features/auth/stores/authStore";
import { FolderPicker } from "@features/folder-picker/components/FolderPicker";
import {
Box,
Button,
Expand All @@ -13,14 +12,11 @@ import {
Spinner,
Text,
} from "@radix-ui/themes";
import { logger } from "@renderer/lib/logger";
import type { CloudRegion } from "@shared/types/oauth";
import { useMutation } from "@tanstack/react-query";
import { useEffect, useState } from "react";
import { useState } from "react";
import { IS_DEV } from "@/constants/environment";

const log = logger.scope("auth");

export const getErrorMessage = (error: unknown) => {
if (!(error instanceof Error)) {
return "Failed to authenticate";
Expand All @@ -38,53 +34,14 @@ export const getErrorMessage = (error: unknown) => {
return message;
};

const detectWorkspacePath = async () => {
try {
const detectedPath = await window.electronAPI.findReposDirectory();
if (detectedPath) {
return detectedPath;
}
} catch (error) {
log.error("Failed to detect repos directory:", error);
}

return null;
};

export function AuthScreen() {
const [region, setRegion] = useState<CloudRegion>("us");
const [workspace, setWorkspace] = useState("~/workspace");
const [workspaceError, setWorkspaceError] = useState<string | null>(null);

const { loginWithOAuth, setDefaultWorkspace } = useAuthStore();

useEffect(() => {
detectWorkspacePath().then((path) => {
if (path) {
setWorkspace(path);
}
});
}, []);
const { loginWithOAuth } = useAuthStore();

const authMutation = useMutation({
mutationFn: async ({
selectedRegion,
workspace,
}: {
selectedRegion: CloudRegion;
workspace: string;
}) => {
if (!workspace || !workspace.trim()) {
setWorkspaceError("Please select a clone location");
throw new Error("Clone location is required");
}

// Login with OAuth first
mutationFn: async (selectedRegion: CloudRegion) => {
await loginWithOAuth(selectedRegion);

// Then save workspace
setDefaultWorkspace(workspace.trim());
setWorkspaceError(null);
},
});

Expand All @@ -93,8 +50,7 @@ export function AuthScreen() {
authMutation.reset();
await window.electronAPI.oauthCancelFlow();
} else {
setWorkspaceError(null);
authMutation.mutate({ selectedRegion: region, workspace });
authMutation.mutate(region);
}
};

Expand Down Expand Up @@ -144,28 +100,6 @@ export function AuthScreen() {
</Select.Root>
</Flex>

<Flex direction="column" gap="2">
<Text as="label" size="2" weight="medium" color="gray">
Default clone location
</Text>
<FolderPicker
value={workspace}
onChange={setWorkspace}
placeholder="~/repos"
size="2"
/>
<Text size="1" color="gray">
Where repositories will be cloned. This should be the
folder where you usually store your projects.
</Text>
</Flex>

{workspaceError && (
<Callout.Root color="red">
<Callout.Text>{workspaceError}</Callout.Text>
</Callout.Root>
)}

{authMutation.isError && (
<Callout.Root color="red">
<Callout.Text>
Expand All @@ -184,7 +118,6 @@ export function AuthScreen() {

<Button
onClick={handleSignIn}
disabled={!workspace}
variant={"classic"}
size="3"
mt="2"
Expand Down
7 changes: 0 additions & 7 deletions apps/array/src/renderer/features/auth/stores/authStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ interface AuthState {
// OpenAI API key (separate concern, kept for now)
openaiApiKey: string | null;
encryptedOpenaiKey: string | null;
defaultWorkspace: string | null;

// OAuth methods
loginWithOAuth: (region: CloudRegion) => Promise<void>;
Expand All @@ -49,7 +48,6 @@ interface AuthState {

// Other methods
setOpenAIKey: (apiKey: string) => Promise<void>;
setDefaultWorkspace: (workspace: string) => void;
logout: () => void;
}

Expand All @@ -74,7 +72,6 @@ export const useAuthStore = create<AuthState>()(
// OpenAI key
openaiApiKey: null,
encryptedOpenaiKey: null,
defaultWorkspace: null,

loginWithOAuth: async (region: CloudRegion) => {
const result = await window.electronAPI.oauthStartFlow(region);
Expand Down Expand Up @@ -357,9 +354,6 @@ export const useAuthStore = create<AuthState>()(
});
},

setDefaultWorkspace: (workspace: string) => {
set({ defaultWorkspace: workspace });
},
logout: () => {
track(ANALYTICS_EVENTS.USER_LOGGED_OUT);
resetUser();
Expand Down Expand Up @@ -394,7 +388,6 @@ export const useAuthStore = create<AuthState>()(
cloudRegion: state.cloudRegion,
storedTokens: state.storedTokens,
encryptedOpenaiKey: state.encryptedOpenaiKey,
defaultWorkspace: state.defaultWorkspace,
projectId: state.projectId,
}),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,8 @@ const REGION_URLS: Record<CloudRegion, string> = {
export function SettingsView() {
useSetHeaderContent(null);

const {
isAuthenticated,
defaultWorkspace,
setDefaultWorkspace,
cloudRegion,
loginWithOAuth,
logout,
} = useAuthStore();
const { isAuthenticated, cloudRegion, loginWithOAuth, logout } =
useAuthStore();
const isDarkMode = useThemeStore((state) => state.isDarkMode);
const toggleDarkMode = useThemeStore((state) => state.toggleDarkMode);
const {
Expand Down Expand Up @@ -292,32 +286,6 @@ export function SettingsView() {

<Box className="border-gray-6 border-t" />

{/* Clone Location Section */}
<Flex direction="column" gap="3">
<Heading size="3">Clone location</Heading>
<Card>
<Flex direction="column" gap="3">
<Flex direction="column" gap="2">
<Text size="1" weight="medium">
Default clone location
</Text>
<FolderPicker
value={defaultWorkspace || ""}
onChange={setDefaultWorkspace}
placeholder="~/repos"
size="1"
/>
<Text size="1" color="gray">
Default directory where repositories will be cloned. This
should be the folder where you usually store your projects.
</Text>
</Flex>
</Flex>
</Card>
</Flex>

<Box className="border-gray-6 border-t" />

{/* Workspace Storage Section */}
<Flex direction="column" gap="3">
<Heading size="3">Workspace storage</Heading>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { useAuthStore } from "@features/auth/stores/authStore";
import { useTaskExecutionStore } from "@features/task-detail/stores/taskExecutionStore";
import { useTasks } from "@features/tasks/hooks/useTasks";
import type { Task } from "@shared/types";
import { cloneStore } from "@stores/cloneStore";
import { expandTildePath } from "@utils/path";
import { getTaskRepository } from "@utils/repository";
import { useEffect, useMemo } from "react";

Expand All @@ -14,7 +12,6 @@ interface UseTaskDataParams {

export function useTaskData({ taskId, initialTask }: UseTaskDataParams) {
const { data: tasks = [] } = useTasks();
const { defaultWorkspace } = useAuthStore();
const initializeRepoPath = useTaskExecutionStore(
(state) => state.initializeRepoPath,
);
Expand All @@ -39,18 +36,10 @@ export function useTaskData({ taskId, initialTask }: UseTaskDataParams) {

const repository = getTaskRepository(task);

// Use the stored repoPath if available, otherwise fall back to derived path
// Use the stored repoPath
const derivedPath = useMemo(() => {
// Prioritize the stored repoPath
if (repoPath) {
return repoPath;
}

// Fall back to deriving from workspace + repository (legacy behavior)
if (!repository || !defaultWorkspace) return null;
const expandedWorkspace = expandTildePath(defaultWorkspace);
return `${expandedWorkspace}/${repository.split("/")[1]}`;
}, [repoPath, repository, defaultWorkspace]);
return repoPath;
}, [repoPath]);

const isCloning = cloneStore((state) =>
repository ? state.isCloning(repository) : false,
Expand Down Expand Up @@ -80,6 +69,5 @@ export function useTaskData({ taskId, initialTask }: UseTaskDataParams) {
derivedPath,
isCloning,
cloneProgress,
defaultWorkspace,
};
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import { useAuthStore } from "@features/auth/stores/authStore";
import { useSettingsStore } from "@features/settings/stores/settingsStore";
import type { Task, WorkspaceMode } from "@shared/types";
import { repositoryWorkspaceStore } from "@stores/repositoryWorkspaceStore";
import { useTaskDirectoryStore } from "@stores/taskDirectoryStore";
import { expandTildePath } from "@utils/path";
import { getTaskRepository } from "@utils/repository";
import { create } from "zustand";
import { persist } from "zustand/middleware";

const derivePath = (workspace: string, repo: string) =>
`${expandTildePath(workspace)}/${repo}`;

interface TaskExecutionState {
repoPath: string | null;
repoExists: boolean | null;
Expand Down Expand Up @@ -127,33 +122,7 @@ export const useTaskExecutionStore = create<TaskExecutionStore>()(
.catch(() => {
store.updateTaskState(taskId, { repoExists: false });
});
return;
}

if (!repository) {
return;
}

const { defaultWorkspace } = useAuthStore.getState();

if (!defaultWorkspace) {
return;
}

const path = derivePath(defaultWorkspace, repository.split("/")[1]);

void store.setRepoPath(taskId, path);

repositoryWorkspaceStore.getState().selectRepository(repository);

window.electronAPI
?.validateRepo(path)
.then((exists) => {
store.updateTaskState(taskId, { repoExists: exists });
})
.catch(() => {
store.updateTaskState(taskId, { repoExists: false });
});
},

revalidateRepo: async (taskId: string) => {
Expand Down
18 changes: 1 addition & 17 deletions apps/array/src/renderer/stores/navigationStore.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { useAuthStore } from "@features/auth/stores/authStore";
import { useTaskExecutionStore } from "@features/task-detail/stores/taskExecutionStore";
import { useWorkspaceStore } from "@features/workspace/stores/workspaceStore";
import { track } from "@renderer/lib/analytics";
import { logger } from "@renderer/lib/logger";
import type { Task, WorkspaceMode } from "@shared/types";
import { useRegisteredFoldersStore } from "@stores/registeredFoldersStore";
import { useTaskDirectoryStore } from "@stores/taskDirectoryStore";
import { expandTildePath } from "@utils/path";
import { getTaskRepository } from "@utils/repository";
import { create } from "zustand";
import { ANALYTICS_EVENTS } from "@/types/analytics";
Expand Down Expand Up @@ -77,24 +75,10 @@ export const useNavigationStore = create<NavigationStore>((set, get) => {
});

const repoKey = getTaskRepository(task) ?? undefined;
let directory = useTaskDirectoryStore
const directory = useTaskDirectoryStore
.getState()
.getTaskDirectory(task.id, repoKey);

// If no directory found, try to derive from defaultWorkspace
if (!directory && repoKey) {
const { defaultWorkspace } = useAuthStore.getState();
if (defaultWorkspace) {
const repoName = repoKey.split("/")[1];
const derivedPath = `${expandTildePath(defaultWorkspace)}/${repoName}`;
// Validate that this path exists
const exists = await window.electronAPI?.validateRepo(derivedPath);
if (exists) {
directory = derivedPath;
}
}
}

if (directory) {
try {
await useRegisteredFoldersStore.getState().addFolder(directory);
Expand Down
Loading