Todo work
Some checks failed
Lint / lint (push) Failing after 10s

This commit is contained in:
Julien Valverdé
2025-01-15 20:09:00 +01:00
parent f3f44d9abe
commit 3813e63982
11 changed files with 153 additions and 13 deletions

View File

@@ -0,0 +1,19 @@
import { ThSchema } from "@thilawyn/thilaschema"
import { Schema } from "effect"
export class Todo extends Schema.Class<Todo>("Todo")({
id: Schema.String,
content: Schema.String,
completedAt: Schema.DateTimeUtcFromSelf,
}) {}
export const TodoFromJsonStruct = Schema.Struct({
...Todo.fields,
completedAt: Schema.DateTimeUtc,
}).pipe(
ThSchema.assertEncodedJsonifiable
)
export const TodoFromJson = TodoFromJsonStruct.pipe(Schema.compose(Todo))

View File

@@ -1 +1,2 @@
export * as Post from "./Post"
export * as Todo from "./Todo"

View File

@@ -1,10 +1,9 @@
import { Reffuse } from "@/reffuse"
import { FetchData } from "@/services"
import { PostsContext } from "@/views/posts/reffuse"
import { PostsState } from "@/views/posts/services"
import { VPosts } from "@/views/posts/VPosts"
import { TodosContext } from "@/todos/reffuse"
import { TodosState } from "@/todos/services"
import { VTodos } from "@/todos/views/VTodos"
import { createFileRoute } from "@tanstack/react-router"
import { Effect } from "effect"
import { Layer } from "effect"
import { useMemo } from "react"
export const Route = createFileRoute("/")({
@@ -13,17 +12,16 @@ export const Route = createFileRoute("/")({
function Index() {
const postsLayer = Reffuse.useMemo(FetchData.FetchData.pipe(
Effect.flatMap(({ fetchPosts }) => fetchPosts),
Effect.map(PostsState.make),
))
const todosLayer = useMemo(() => Layer.empty.pipe(
Layer.provideMerge(TodosState.make("todos"))
), [])
return (
<div className="container mx-auto">
<PostsContext.Provider layer={postsLayer}>
<VPosts />
</PostsContext.Provider>
<TodosContext.Provider layer={todosLayer}>
<VTodos />
</TodosContext.Provider>
</div>
)

View File

@@ -0,0 +1,8 @@
import { GlobalContext } from "@/reffuse"
import { ReffuseContext } from "@thilawyn/reffuse"
import { make } from "@thilawyn/reffuse/Reffuse"
import { TodosState } from "./services"
export const TodosContext = ReffuseContext.make<TodosState.TodosState>()
export const Reffuse = make(GlobalContext, TodosContext)

View File

@@ -0,0 +1,64 @@
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"
export class TodosState extends Context.Tag("TodosState")<TodosState, {
readonly todos: SubscriptionRef.SubscriptionRef<Chunk.Chunk<Todo.Todo>>
readonly readFromLocalStorage: Effect.Effect<void, Cause.NoSuchElementException | PlatformError | ParseResult.ParseError>
readonly saveToLocalStorage: Effect.Effect<void, PlatformError | ParseResult.ParseError>
readonly prepend: (todo: Todo.Todo) => Effect.Effect<void>
readonly remove: (index: number) => Effect.Effect<void>
// readonly moveUp: (index: number) => Effect.Effect<void, Cause.NoSuchElementException>
// readonly moveDown: (index: number) => Effect.Effect<void, Cause.NoSuchElementException>
}>() {}
export const make = (key: string) => Layer.effect(TodosState, Effect.gen(function*() {
const todos = yield* SubscriptionRef.make(Chunk.empty<Todo.Todo>())
const readFromLocalStorage = KeyValueStore.KeyValueStore.pipe(
Effect.flatMap(kv => kv.get(key)),
Effect.flatMap(identity),
Effect.flatMap(Schema.parseJson().pipe(
Schema.compose(Schema.Chunk(Todo.TodoFromJson)),
Schema.decode,
)),
Effect.flatMap(v => Ref.set(todos, v)),
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
)
const saveToLocalStorage = Effect.all([KeyValueStore.KeyValueStore, todos]).pipe(
Effect.flatMap(([kv, values]) => values.pipe(
Schema.parseJson().pipe(
Schema.compose(Schema.Chunk(Todo.TodoFromJson)),
Schema.encode,
),
Effect.flatMap(v => kv.set(key, v)),
)),
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
)
const prepend = (todo: Todo.Todo) => Ref.update(todos, Chunk.prepend(todo))
const remove = (index: number) => Ref.update(todos, Chunk.remove(index))
// const moveUp = (index: number) => Effect.gen(function*() {
// })
yield* readFromLocalStorage
return {
todos,
readFromLocalStorage,
saveToLocalStorage,
prepend,
remove,
}
}))

View File

@@ -0,0 +1 @@
export * as TodosState from "./TodosState"

View File

@@ -0,0 +1,21 @@
import { Todo } from "@/domain"
import { Reffuse } from "../reffuse"
export interface VTodoProps {
readonly index: number
readonly todo: Todo.Todo
}
export function VTodo({ index, todo }: VTodoProps) {
const runSync = Reffuse.useRunSync()
return (
<div className="">
</div>
)
}

View File

@@ -0,0 +1,21 @@
import { Effect, Stream } from "effect"
import { Reffuse } from "../reffuse"
import { TodosState } from "../services"
export function VTodos() {
// Sync changes to the todos with the local storage
Reffuse.useFork(TodosState.TodosState.pipe(
Effect.flatMap(state =>
Stream.runForEach(state.todos, () => state.saveToLocalStorage)
)
))
const todosRef = Reffuse.useMemo(TodosState.TodosState.pipe(Effect.map(state => state.todos)))
const [todos] = Reffuse.useRefState(todosRef)
return <></>
}