Skip to content

Commit ea86ed8

Browse files
committed
They have to move the folder back or remove the repo
1 parent 2ccf38d commit ea86ed8

File tree

5 files changed

+86
-171
lines changed

5 files changed

+86
-171
lines changed

apps/array/src/main/services/folders/schemas.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,6 @@ export const updateFolderAccessedInput = z.object({
2828
folderId: z.string(),
2929
});
3030

31-
export const updateFolderPathInput = z.object({
32-
folderId: z.string(),
33-
newPath: z.string(),
34-
});
35-
36-
export const updateFolderPathOutput = registeredFolderWithExistsSchema;
37-
3831
export const cleanupOrphanedWorktreesInput = z.object({
3932
mainRepoPath: z.string(),
4033
});
@@ -57,8 +50,6 @@ export type RemoveFolderInput = z.infer<typeof removeFolderInput>;
5750
export type UpdateFolderAccessedInput = z.infer<
5851
typeof updateFolderAccessedInput
5952
>;
60-
export type UpdateFolderPathInput = z.infer<typeof updateFolderPathInput>;
61-
export type UpdateFolderPathOutput = z.infer<typeof updateFolderPathOutput>;
6253
export type CleanupOrphanedWorktreesInput = z.infer<
6354
typeof cleanupOrphanedWorktreesInput
6455
>;

apps/array/src/main/services/folders/service.ts

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -140,53 +140,6 @@ export class FoldersService {
140140
}
141141
}
142142

143-
async updateFolderPath(
144-
folderId: string,
145-
newPath: string,
146-
): Promise<RegisteredFolder & { exists: boolean }> {
147-
const folders = foldersStore.get("folders", []);
148-
const folder = folders.find((f) => f.id === folderId);
149-
150-
if (!folder) {
151-
throw new Error(`Folder with ID ${folderId} not found`);
152-
}
153-
154-
// Validate the new path exists
155-
if (!fs.existsSync(newPath)) {
156-
throw new Error(`Path does not exist: ${newPath}`);
157-
}
158-
159-
// Check if it's a git repository
160-
const isRepo = await isGitRepository(newPath);
161-
if (!isRepo) {
162-
throw new Error("The selected folder is not a git repository");
163-
}
164-
165-
// Update the folder
166-
folder.path = newPath;
167-
folder.name = path.basename(newPath);
168-
folder.lastAccessed = new Date().toISOString();
169-
foldersStore.set("folders", folders);
170-
171-
// Update all task associations that reference this folder
172-
const associations = foldersStore.get("taskAssociations", []);
173-
let hasChanges = false;
174-
for (const assoc of associations) {
175-
if (assoc.folderId === folderId) {
176-
assoc.folderPath = newPath;
177-
hasChanges = true;
178-
}
179-
}
180-
if (hasChanges) {
181-
foldersStore.set("taskAssociations", associations);
182-
log.debug(
183-
`Updated folder path for ${associations.filter((a) => a.folderId === folderId).length} task associations`,
184-
);
185-
}
186-
187-
return { ...folder, exists: true };
188-
}
189-
190143
async cleanupOrphanedWorktrees(
191144
mainRepoPath: string,
192145
): Promise<CleanupOrphanedWorktreesOutput> {

apps/array/src/main/trpc/routers/folders.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import {
88
getFoldersOutput,
99
removeFolderInput,
1010
updateFolderAccessedInput,
11-
updateFolderPathInput,
12-
updateFolderPathOutput,
1311
} from "../../services/folders/schemas.js";
1412
import type { FoldersService } from "../../services/folders/service.js";
1513
import { publicProcedure, router } from "../trpc.js";
@@ -41,13 +39,6 @@ export const foldersRouter = router({
4139
return getService().updateFolderAccessed(input.folderId);
4240
}),
4341

44-
updateFolderPath: publicProcedure
45-
.input(updateFolderPathInput)
46-
.output(updateFolderPathOutput)
47-
.mutation(({ input }) => {
48-
return getService().updateFolderPath(input.folderId, input.newPath);
49-
}),
50-
5142
cleanupOrphanedWorktrees: publicProcedure
5243
.input(cleanupOrphanedWorktreesInput)
5344
.output(cleanupOrphanedWorktreesOutput)

apps/array/src/renderer/features/folder-picker/components/FolderPicker.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,13 @@ interface FolderPickerProps {
1414
onChange: (path: string) => void;
1515
placeholder?: string;
1616
size?: Responsive<"1" | "2">;
17-
skipRegister?: boolean;
1817
}
1918

2019
export function FolderPicker({
2120
value,
2221
onChange,
2322
placeholder = "Select folder...",
2423
size = "1",
25-
skipRegister = false,
2624
}: FolderPickerProps) {
2725
const recentFolders = useRegisteredFoldersStore((state) =>
2826
state.getRecentFolders(),
@@ -49,9 +47,7 @@ export function FolderPicker({
4947
const handleOpenFilePicker = async () => {
5048
const selectedPath = await trpcVanilla.os.selectDirectory.query();
5149
if (selectedPath) {
52-
if (!skipRegister) {
53-
await addFolder(selectedPath);
54-
}
50+
await addFolder(selectedPath);
5551
onChange(selectedPath);
5652
}
5753
};

apps/array/src/renderer/features/settings/components/FolderSettingsView.tsx

Lines changed: 85 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,33 @@
1-
import { FolderPicker } from "@features/folder-picker/components/FolderPicker";
2-
import { useWorkspaceStore } from "@features/workspace/stores/workspaceStore";
31
import { useSetHeaderContent } from "@hooks/useSetHeaderContent";
42
import { Warning } from "@phosphor-icons/react";
53
import {
64
Box,
75
Button,
86
Callout,
97
Card,
8+
Code,
109
Flex,
1110
Heading,
1211
Text,
1312
} from "@radix-ui/themes";
1413
import { logger } from "@renderer/lib/logger";
1514
import { useNavigationStore } from "@renderer/stores/navigationStore";
1615
import { useRegisteredFoldersStore } from "@renderer/stores/registeredFoldersStore";
17-
import { trpcVanilla } from "@renderer/trpc";
18-
import { useMutation, useQueryClient } from "@tanstack/react-query";
19-
import { useEffect, useState } from "react";
16+
import { useState } from "react";
2017

2118
const log = logger.scope("folder-settings");
2219

2320
export function FolderSettingsView() {
2421
useSetHeaderContent(null);
2522

2623
const { view, navigateToTaskInput } = useNavigationStore();
27-
const { folders, removeFolder, loadFolders } = useRegisteredFoldersStore();
28-
const queryClient = useQueryClient();
24+
const { folders, removeFolder } = useRegisteredFoldersStore();
2925

3026
const folderId = view.type === "folder-settings" ? view.folderId : undefined;
3127
const folder = folders.find((f) => f.id === folderId);
3228

33-
const [newPath, setNewPath] = useState(folder?.path ?? "");
3429
const [error, setError] = useState<string | null>(null);
3530

36-
// Reset form when folder changes
37-
useEffect(() => {
38-
if (folder) {
39-
setNewPath(folder.path);
40-
}
41-
}, [folder]);
42-
43-
const updatePathMutation = useMutation({
44-
mutationFn: async (path: string) => {
45-
if (!folderId) throw new Error("No folder selected");
46-
return await trpcVanilla.folders.updateFolderPath.mutate({
47-
folderId,
48-
newPath: path,
49-
});
50-
},
51-
onSuccess: async (_, newPath) => {
52-
await loadFolders();
53-
queryClient.invalidateQueries({ queryKey: ["folders"] });
54-
setError(null);
55-
56-
if (folderId) {
57-
const { workspaces, updateWorkspace } = useWorkspaceStore.getState();
58-
for (const [taskId, workspace] of Object.entries(workspaces)) {
59-
if (workspace.folderId === folderId) {
60-
updateWorkspace(taskId, { ...workspace, folderPath: newPath });
61-
}
62-
}
63-
await useWorkspaceStore.getState().loadWorkspaces();
64-
}
65-
},
66-
onError: (err) => {
67-
log.error("Failed to update folder path:", err);
68-
setError(err instanceof Error ? err.message : "Failed to update path");
69-
},
70-
});
71-
72-
const handlePathChange = async (path: string) => {
73-
setNewPath(path);
74-
if (folder && !folder.exists && path !== folder.path) {
75-
await updatePathMutation.mutateAsync(path);
76-
}
77-
};
78-
79-
const handleUpdatePath = async () => {
80-
if (!newPath || newPath === folder?.path) return;
81-
await updatePathMutation.mutateAsync(newPath);
82-
};
83-
8431
const handleRemoveFolder = async () => {
8532
if (!folderId) return;
8633
try {
@@ -107,75 +54,112 @@ export function FolderSettingsView() {
10754
);
10855
}
10956

110-
return (
111-
<Box height="100%" overflowY="auto">
112-
<Box p="6" style={{ maxWidth: "600px", margin: "0 auto" }}>
113-
<Flex direction="column" gap="6">
114-
<Flex direction="column" gap="2">
115-
<Heading size="4">Repository Settings</Heading>
116-
<Text size="1" color="gray">
117-
Manage settings for {folder.name}
118-
</Text>
119-
</Flex>
57+
// When folder doesn't exist, show message to restore or remove
58+
if (!folder.exists) {
59+
return (
60+
<Box height="100%" overflowY="auto">
61+
<Box p="6" style={{ maxWidth: "600px", margin: "0 auto" }}>
62+
<Flex direction="column" gap="6">
63+
<Flex direction="column" gap="2">
64+
<Heading size="4">Repository Not Found</Heading>
65+
<Text size="1" color="gray">
66+
{folder.name}
67+
</Text>
68+
</Flex>
12069

121-
{!folder.exists && (
12270
<Callout.Root color="amber">
12371
<Callout.Icon>
12472
<Warning />
12573
</Callout.Icon>
12674
<Callout.Text>
12775
<Flex direction="column" gap="1">
12876
<Text weight="medium">
129-
Repository path needs to be updated
77+
The repository folder could not be found
13078
</Text>
13179
<Text size="1">
132-
The folder at "{folder.path}" was not found. If you moved
133-
this repository, select the new location below to continue
134-
working on tasks in this repository.
80+
The folder at <Code>{folder.path}</Code> no longer exists or
81+
has been moved.
13582
</Text>
13683
</Flex>
13784
</Callout.Text>
13885
</Callout.Root>
139-
)}
14086

141-
{error && (
142-
<Callout.Root color="red">
143-
<Callout.Text>{error}</Callout.Text>
144-
</Callout.Root>
145-
)}
87+
{error && (
88+
<Callout.Root color="red">
89+
<Callout.Text>{error}</Callout.Text>
90+
</Callout.Root>
91+
)}
14692

147-
<Flex direction="column" gap="3">
148-
<Heading size="3">Location</Heading>
14993
<Card>
15094
<Flex direction="column" gap="4">
15195
<Flex direction="column" gap="2">
15296
<Text size="1" weight="medium">
153-
Root path
97+
Option 1: Restore the folder
15498
</Text>
155-
<FolderPicker
156-
value={newPath}
157-
onChange={handlePathChange}
158-
placeholder={folder.path}
159-
size="1"
160-
skipRegister
161-
/>
16299
<Text size="1" color="gray">
163-
The main repository directory
100+
Move or restore the repository folder back to its original
101+
location:
102+
</Text>
103+
<Code size="1">{folder.path}</Code>
104+
</Flex>
105+
</Flex>
106+
</Card>
107+
108+
<Card>
109+
<Flex direction="column" gap="4">
110+
<Flex direction="column" gap="2">
111+
<Text size="1" weight="medium">
112+
Option 2: Remove the repository
113+
</Text>
114+
<Text size="1" color="gray">
115+
This will remove the repository from Array, including all
116+
associated tasks and their workspaces. This action cannot be
117+
undone.
164118
</Text>
165119
</Flex>
166-
{newPath && newPath !== folder.path && folder.exists && (
167-
<Button
168-
variant="classic"
169-
size="1"
170-
onClick={handleUpdatePath}
171-
disabled={updatePathMutation.isPending}
172-
style={{ alignSelf: "flex-start" }}
173-
>
174-
{updatePathMutation.isPending
175-
? "Updating..."
176-
: "Update path"}
177-
</Button>
178-
)}
120+
<Button
121+
variant="soft"
122+
color="red"
123+
size="1"
124+
onClick={handleRemoveFolder}
125+
style={{ alignSelf: "flex-start" }}
126+
>
127+
Remove repository
128+
</Button>
129+
</Flex>
130+
</Card>
131+
</Flex>
132+
</Box>
133+
</Box>
134+
);
135+
}
136+
137+
// Normal settings view when folder exists
138+
return (
139+
<Box height="100%" overflowY="auto">
140+
<Box p="6" style={{ maxWidth: "600px", margin: "0 auto" }}>
141+
<Flex direction="column" gap="6">
142+
<Flex direction="column" gap="2">
143+
<Heading size="4">Repository Settings</Heading>
144+
<Text size="1" color="gray">
145+
Manage settings for {folder.name}
146+
</Text>
147+
</Flex>
148+
149+
{error && (
150+
<Callout.Root color="red">
151+
<Callout.Text>{error}</Callout.Text>
152+
</Callout.Root>
153+
)}
154+
155+
<Flex direction="column" gap="3">
156+
<Heading size="3">Location</Heading>
157+
<Card>
158+
<Flex direction="column" gap="2">
159+
<Text size="1" weight="medium">
160+
Root path
161+
</Text>
162+
<Code size="1">{folder.path}</Code>
179163
</Flex>
180164
</Card>
181165
</Flex>

0 commit comments

Comments
 (0)