| author | |
| committer | |
| log | 4fbc4f9b20cc4d2e991349ce30b0118473de4621 |
| tree | bae9892d21fbb9b58e63bb263db1cba1ca7d7e0f |
| parent | 779c2374876884992c7cdf059556acd255db824e |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: codex <codex@users.noreply.github.com>2 files changed, 67 insertions(+), 9 deletions(-)
apps/mobile/src/features/threads/projectThreadCreationValidation.ts created+56| ... | @@ -0,0 +1,56 @@ | ||
| 1 | import { EnvironmentId, ProjectId } from "@t3tools/contracts"; | ||
| 2 | import * as Schema from "effect/Schema"; | ||
| 3 | |||
| 4 | export class ProjectThreadTaskRequiredError extends Schema.TaggedErrorClass<ProjectThreadTaskRequiredError>()( | ||
| 5 | "ProjectThreadTaskRequiredError", | ||
| 6 | { | ||
| 7 | environmentId: EnvironmentId, | ||
| 8 | projectId: ProjectId, | ||
| 9 | environmentMode: Schema.Literals(["local", "worktree"]), | ||
| 10 | }, | ||
| 11 | ) { | ||
| 12 | override get message(): string { | ||
| 13 | return "Enter a task before starting the thread."; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | export class ProjectThreadBaseBranchRequiredError extends Schema.TaggedErrorClass<ProjectThreadBaseBranchRequiredError>()( | ||
| 18 | "ProjectThreadBaseBranchRequiredError", | ||
| 19 | { | ||
| 20 | environmentId: EnvironmentId, | ||
| 21 | projectId: ProjectId, | ||
| 22 | }, | ||
| 23 | ) { | ||
| 24 | override get message(): string { | ||
| 25 | return "Select a base branch before creating a worktree."; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | export const ProjectThreadCreationValidationError = Schema.Union([ | ||
| 30 | ProjectThreadTaskRequiredError, | ||
| 31 | ProjectThreadBaseBranchRequiredError, | ||
| 32 | ]); | ||
| 33 | export type ProjectThreadCreationValidationError = typeof ProjectThreadCreationValidationError.Type; | ||
| 34 | |||
| 35 | export function validateProjectThreadCreation(input: { | ||
| 36 | readonly environmentId: EnvironmentId; | ||
| 37 | readonly projectId: ProjectId; | ||
| 38 | readonly environmentMode: "local" | "worktree"; | ||
| 39 | readonly branch: string | null; | ||
| 40 | readonly initialMessageText: string; | ||
| 41 | }): ProjectThreadCreationValidationError | null { | ||
| 42 | if (input.initialMessageText.trim().length === 0) { | ||
| 43 | return new ProjectThreadTaskRequiredError({ | ||
| 44 | environmentId: input.environmentId, | ||
| 45 | projectId: input.projectId, | ||
| 46 | environmentMode: input.environmentMode, | ||
| 47 | }); | ||
| 48 | } | ||
| 49 | if (input.environmentMode === "worktree" && !input.branch) { | ||
| 50 | return new ProjectThreadBaseBranchRequiredError({ | ||
| 51 | environmentId: input.environmentId, | ||
| 52 | projectId: input.projectId, | ||
| 53 | }); | ||
| 54 | } | ||
| 55 | return null; | ||
| 56 | } | ||
apps/mobile/src/features/threads/use-project-actions.ts+11-9| ... | @@ -21,6 +21,7 @@ import { makeTurnCommandMetadata } from "../../lib/commandMetadata"; | ... | @@ -21,6 +21,7 @@ import { makeTurnCommandMetadata } from "../../lib/commandMetadata"; |
| 21 | import { uuidv4 } from "../../lib/uuid"; | 21 | import { uuidv4 } from "../../lib/uuid"; |
| 22 | import { useAtomCommand } from "../../state/use-atom-command"; | 22 | import { useAtomCommand } from "../../state/use-atom-command"; |
| 23 | import { setPendingConnectionError } from "../../state/use-remote-environment-registry"; | 23 | import { setPendingConnectionError } from "../../state/use-remote-environment-registry"; |
| 24 | import { validateProjectThreadCreation } from "./projectThreadCreationValidation"; | ||
| 24 | 25 | ||
| 25 | function deriveThreadTitleFromPrompt(value: string): string { | 26 | function deriveThreadTitleFromPrompt(value: string): string { |
| 26 | const trimmed = value.trim(); | 27 | const trimmed = value.trim(); |
| ... | @@ -52,15 +53,16 @@ export function useCreateProjectThread() { | ... | @@ -52,15 +53,16 @@ export function useCreateProjectThread() { |
| 52 | const initialMessageText = input.initialMessageText.trim(); | 53 | const initialMessageText = input.initialMessageText.trim(); |
| 53 | const nextTitle = deriveThreadTitleFromPrompt(input.initialMessageText); | 54 | const nextTitle = deriveThreadTitleFromPrompt(input.initialMessageText); |
| 54 | 55 | ||
| 55 | if (initialMessageText.length === 0) { | 56 | const validationError = validateProjectThreadCreation({ |
| 56 | const error = new Error("Enter a task before starting the thread."); | 57 | environmentId: input.project.environmentId, |
| 57 | setPendingConnectionError(error.message); | 58 | projectId: input.project.id, |
| 58 | return AsyncResult.failure(Cause.fail(error)); | 59 | environmentMode: input.envMode, |
| 59 | } | 60 | branch: input.branch, |
| 60 | if (input.envMode === "worktree" && !input.branch) { | 61 | initialMessageText, |
| 61 | const error = new Error("Select a base branch before creating a worktree."); | 62 | }); |
| 62 | setPendingConnectionError(error.message); | 63 | if (validationError !== null) { |
| 63 | return AsyncResult.failure(Cause.fail(error)); | 64 | setPendingConnectionError(validationError.message); |
| 65 | return AsyncResult.failure(Cause.fail(validationError)); | ||
| 64 | } | 66 | } |
| 65 | 67 | ||
| 66 | const isWorktree = input.envMode === "worktree"; | 68 | const isWorktree = input.envMode === "worktree"; |