diff --git a/apps/frontend/Todo.ts b/apps/frontend/Todo.ts index c904e5a..36c5c07 100644 --- a/apps/frontend/Todo.ts +++ b/apps/frontend/Todo.ts @@ -2,6 +2,7 @@ import * as A from "@effect-ts-demo/core/ext/Array" import { array, constructor, + identity, Model, literal, NonEmptyString, @@ -15,10 +16,12 @@ import { These, union, reasonableString, + parseStringE, + leafE, } from "@effect-ts-demo/core/ext/Schema" import * as Todo from "@effect-ts-demo/todo-client/Tasks" import { TaskId, TaskListId } from "@effect-ts-demo/todo-client/Tasks" -import { constant, flow, identity, pipe } from "@effect-ts/core/Function" +import { constant, flow, pipe, identity as ident } from "@effect-ts/core/Function" import * as O from "@effect-ts/core/Option" import * as ORD from "@effect-ts/core/Ord" import { Lens } from "@effect-ts/monocle" @@ -151,8 +154,12 @@ const isOrder = (u: any): u is Order & NonEmptyString => u in orders export const Order = nonEmptyString[">>>"]( pipe( identity(isOrder), - parser((x) => (isOrder(x) ? These.succeed(x) : These.fail("not order"))), - constructor((x) => (isOrder(x) ? These.succeed(x) : These.fail("not order"))) + parser((x) => + isOrder(x) ? These.succeed(x) : These.fail(leafE(parseStringE("not order"))) + ), + constructor((x) => + isOrder(x) ? These.succeed(x) : These.fail(leafE(parseStringE("not order"))) + ) ) ) // TODO @@ -162,8 +169,12 @@ const isOrderDir = (u: any): u is OrderDir & NonEmptyString => u in orders export const OrderDir = nonEmptyString[">>>"]( pipe( identity(isOrderDir), - parser((x) => (isOrderDir(x) ? These.succeed(x) : These.fail("not order"))), - constructor((x) => (isOrderDir(x) ? These.succeed(x) : These.fail("not order"))) + parser((x) => + isOrderDir(x) ? These.succeed(x) : These.fail(leafE(parseStringE("not order"))) + ), + constructor((x) => + isOrderDir(x) ? These.succeed(x) : These.fail(leafE(parseStringE("not order"))) + ) ) ) // TODO @@ -188,7 +199,7 @@ export function filterByCategory(category: TaskView | string) { return A.filter((t: Todo.Task) => t.listId === "inbox") } case "all": { - return identity + return ident } default: return A.filter((t: Todo.Task) => t.listId === category) diff --git a/apps/frontend/features/Tasks/FolderList/FolderList.tsx b/apps/frontend/features/Tasks/FolderList/FolderList.tsx index 0659d17..b054143 100644 --- a/apps/frontend/features/Tasks/FolderList/FolderList.tsx +++ b/apps/frontend/features/Tasks/FolderList/FolderList.tsx @@ -55,7 +55,7 @@ function TLG(g: Todo.TaskListGroup) { > )} diff --git a/apps/frontend/features/Tasks/FolderList/index.tsx b/apps/frontend/features/Tasks/FolderList/index.tsx index e28432f..39136d8 100644 --- a/apps/frontend/features/Tasks/FolderList/index.tsx +++ b/apps/frontend/features/Tasks/FolderList/index.tsx @@ -57,14 +57,14 @@ const FolderListView = ({ category }: { category: O.Option }) => A.map( ({ slug, tasks }) => new TaskListView({ - title: toUpperCaseFirst(slug) as S.NonEmptyString, + title: toUpperCaseFirst(slug) as S.ReasonableString, slug, count: tasks.length, }) ) ), new TaskListView({ - title: "Tasks" as S.NonEmptyString, + title: "Tasks" as S.ReasonableString, slug: "tasks", count: unfilteredTasks["|>"](filterByCategory("inbox")).length, }), diff --git a/apps/frontend/features/Tasks/data.ts b/apps/frontend/features/Tasks/data.ts index 85e05f6..8123758 100644 --- a/apps/frontend/features/Tasks/data.ts +++ b/apps/frontend/features/Tasks/data.ts @@ -77,8 +77,8 @@ export function useTasks() { } const newTask = - (v: Todo.TaskView | S.NonEmptyString, listId: Todo.TaskListIdU = "inbox") => - (newTitle: S.NonEmptyString) => + (v: Todo.TaskView | S.ReasonableString, listId: Todo.TaskListIdU = "inbox") => + (newTitle: S.ReasonableString) => TodoClient.TasksClient.CreateTask({ title: newTitle, isFavorite: v === "important", @@ -86,7 +86,7 @@ const newTask = listId, }) export function useNewTask( - v: Todo.TaskView | S.NonEmptyString, + v: Todo.TaskView | S.ReasonableString, listId?: Todo.TaskListId ) { return useFetch(newTask(v, listId)) @@ -224,7 +224,7 @@ export function useTaskCommandsResolved(t: Todo.Task) { } } -const parseNES = Parser.for(S.nonEmptyString)["|>"](S.condemnFail) +const parseRS = Parser.for(S.reasonableString)["|>"](S.condemnFail) export function useTaskCommands(id: Todo.TaskId) { const modifyTasks = useModifyTasks() @@ -263,7 +263,7 @@ export function useTaskCommands(id: Todo.TaskId) { function updateStepTitle(t: Todo.Task) { return (s: Todo.Step) => flow( - parseNES, + parseRS, T.map((stepTitle) => t["|>"](Todo.Task.updateStep(s, stepTitle))), T.chain(updateAndRefreshTask) ) @@ -287,7 +287,7 @@ export function useTaskCommands(id: Todo.TaskId) { function addNewTaskStep(t: Todo.Task) { return flow( - parseNES, + parseRS, T.map((title) => t["|>"](Todo.Task.addStep(title))), T.chain(updateAndRefreshTask) ) @@ -303,7 +303,7 @@ export function useTaskCommands(id: Todo.TaskId) { function setTitle(t: Todo.Task) { return flow( - parseNES, + parseRS, T.map((v) => t["|>"](Todo.Task.lens["|>"](Lens.prop("title")).set(v))), T.chain(updateAndRefreshTask) ) @@ -321,7 +321,7 @@ export function useTaskCommands(id: Todo.TaskId) { return (note: string | null) => pipe( EO.fromNullable(note), - EO.chainEffect(parseNES), + EO.chainEffect(parseRS), T.chain((note) => updateAndRefreshTask({ id: t.id, note })) ) } diff --git a/packages/core/ext/Schema/_api/string.ts b/packages/core/ext/Schema/_api/string.ts index 91388e6..17ac239 100644 --- a/packages/core/ext/Schema/_api/string.ts +++ b/packages/core/ext/Schema/_api/string.ts @@ -28,7 +28,8 @@ export interface ReasonableStringBrand { readonly ReasonableString: unique symbol } -export type ReasonableString = S.NonEmptyString & ReasonableStringBrand +// TODO: Evaluate if it makes sense to inherit the others too. +export type ReasonableString = TextString & LongString & ReasonableStringBrand export const reasonableStringFromString = pipe( makeConstrainedFromString(1, 256 - 1), @@ -45,7 +46,8 @@ export interface LongStringBrand { readonly LongString: unique symbol } -export type LongString = S.NonEmptyString & LongStringBrand +// TODO: Evaluate if it makes sense to inherit the others too. +export type LongString = TextString & LongStringBrand export const longStringFromString = pipe( makeConstrainedFromString(1, 2048 - 1),