diff --git a/bun.lockb b/bun.lockb index 977532e..cf49da4 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/example/package.json b/packages/example/package.json index f9c93b6..4b15181 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -34,6 +34,9 @@ }, "dependencies": { "@effect/platform": "^0.73.1", - "@effect/platform-browser": "^0.52.1" + "@effect/platform-browser": "^0.52.1", + "@radix-ui/themes": "^3.1.6", + "@typed/id": "^0.17.1", + "mobx": "^6.13.5" } } diff --git a/packages/example/src/domain/Todo.ts b/packages/example/src/domain/Todo.ts index 5eb8474..0683a6e 100644 --- a/packages/example/src/domain/Todo.ts +++ b/packages/example/src/domain/Todo.ts @@ -1,19 +1,28 @@ import { ThSchema } from "@thilawyn/thilaschema" -import { Schema } from "effect" +import { CuidState, DateTimes, GetRandomValues, makeCuid } from "@typed/id" +import { Effect, Schema } from "effect" export class Todo extends Schema.Class("Todo")({ + _tag: Schema.tag("Todo"), id: Schema.String, content: Schema.String, - completedAt: Schema.DateTimeUtcFromSelf, + completedAt: Schema.OptionFromSelf(Schema.DateTimeUtcFromSelf), }) {} export const TodoFromJsonStruct = Schema.Struct({ ...Todo.fields, - completedAt: Schema.DateTimeUtc, + completedAt: Schema.Option(Schema.DateTimeUtc), }).pipe( ThSchema.assertEncodedJsonifiable ) export const TodoFromJson = TodoFromJsonStruct.pipe(Schema.compose(Todo)) + + +export const generateUniqueID = makeCuid.pipe( + Effect.provide(CuidState.layer("@reffuse/example")), + Effect.provide(GetRandomValues.CryptoRandom), + Effect.provide(DateTimes.Default), +) diff --git a/packages/example/src/main.tsx b/packages/example/src/main.tsx index 1f4673e..715d6e6 100644 --- a/packages/example/src/main.tsx +++ b/packages/example/src/main.tsx @@ -3,7 +3,6 @@ import { ReffuseRuntime } from "@thilawyn/reffuse" import { Layer } from "effect" import { StrictMode } from "react" import { createRoot } from "react-dom/client" -import "./index.css" import { GlobalContext } from "./reffuse" import { routeTree } from "./routeTree.gen" import { FetchData } from "./services" diff --git a/packages/example/src/routes/__root.tsx b/packages/example/src/routes/__root.tsx index b152a32..beef033 100644 --- a/packages/example/src/routes/__root.tsx +++ b/packages/example/src/routes/__root.tsx @@ -1,5 +1,8 @@ +import { Container, Flex, Theme } from "@radix-ui/themes" +import "@radix-ui/themes/styles.css" import { createRootRoute, Link, Outlet } from "@tanstack/react-router" import { TanStackRouterDevtools } from "@tanstack/router-devtools" +import "../index.css" export const Route = createRootRoute({ @@ -7,14 +10,18 @@ export const Route = createRootRoute({ }) function Root() { - return <> -
- Index - Time - Count -
+ return ( + + + + Index + Time + Count + + - - - + + + + ) } diff --git a/packages/example/src/routes/index.tsx b/packages/example/src/routes/index.tsx index 8e2bdad..6de5c78 100644 --- a/packages/example/src/routes/index.tsx +++ b/packages/example/src/routes/index.tsx @@ -1,6 +1,7 @@ import { TodosContext } from "@/todos/reffuse" import { TodosState } from "@/todos/services" import { VTodos } from "@/todos/views/VTodos" +import { Container } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { Layer } from "effect" import { useMemo } from "react" @@ -18,11 +19,11 @@ function Index() { return ( -
+ -
+ ) } diff --git a/packages/example/src/todos/services/TodosState.ts b/packages/example/src/todos/services/TodosState.ts index e683ddb..d9b2bea 100644 --- a/packages/example/src/todos/services/TodosState.ts +++ b/packages/example/src/todos/services/TodosState.ts @@ -2,16 +2,17 @@ import { Todo } from "@/domain" import { KeyValueStore } from "@effect/platform" import { BrowserKeyValueStore } from "@effect/platform-browser" import { PlatformError } from "@effect/platform/Error" -import { Cause, Chunk, Context, Effect, identity, Layer, ParseResult, Ref, Schema, SubscriptionRef } from "effect" +import { Chunk, Context, Effect, identity, Layer, ParseResult, Ref, Schema, SubscriptionRef } from "effect" export class TodosState extends Context.Tag("TodosState")> - readonly readFromLocalStorage: Effect.Effect + readonly readFromLocalStorage: Effect.Effect readonly saveToLocalStorage: Effect.Effect readonly prepend: (todo: Todo.Todo) => Effect.Effect + readonly replace: (index: number, todo: Todo.Todo) => Effect.Effect readonly remove: (index: number) => Effect.Effect // readonly moveUp: (index: number) => Effect.Effect // readonly moveDown: (index: number) => Effect.Effect @@ -30,6 +31,8 @@ export const make = (key: string) => Layer.effect(TodosState, Effect.gen(functio )), Effect.flatMap(v => Ref.set(todos, v)), + Effect.catchTag("NoSuchElementException", () => Ref.set(todos, Chunk.empty())), + Effect.provide(BrowserKeyValueStore.layerLocalStorage), ) @@ -46,6 +49,7 @@ export const make = (key: string) => Layer.effect(TodosState, Effect.gen(functio ) const prepend = (todo: Todo.Todo) => Ref.update(todos, Chunk.prepend(todo)) + const replace = (index: number, todo: Todo.Todo) => Ref.update(todos, Chunk.replace(index, todo)) const remove = (index: number) => Ref.update(todos, Chunk.remove(index)) // const moveUp = (index: number) => Effect.gen(function*() { @@ -59,6 +63,7 @@ export const make = (key: string) => Layer.effect(TodosState, Effect.gen(functio readFromLocalStorage, saveToLocalStorage, prepend, + replace, remove, } })) diff --git a/packages/example/src/todos/views/VNewTodo.tsx b/packages/example/src/todos/views/VNewTodo.tsx new file mode 100644 index 0000000..c454ff0 --- /dev/null +++ b/packages/example/src/todos/views/VNewTodo.tsx @@ -0,0 +1,52 @@ +import { Todo } from "@/domain" +import { Box, Button, Card, Flex, TextArea } from "@radix-ui/themes" +import { Effect, Option } from "effect" +import { Reffuse } from "../reffuse" +import { TodosState } from "../services" + + +export function VNewTodo() { + + const runSync = Reffuse.useRunSync() + + const createEmptyTodo = Todo.generateUniqueID.pipe( + Effect.map(id => Todo.Todo.make({ + id, + content: "", + completedAt: Option.none(), + }, true)) + ) + + const todoRef = Reffuse.useRefFromEffect(createEmptyTodo) + const [todo, setTodo] = Reffuse.useRefState(todoRef) + + + return ( + + + +