TodosState
All checks were successful
Lint / lint (push) Successful in 12s

This commit is contained in:
Julien Valverdé
2025-07-02 15:38:27 +02:00
parent 6c8f6622dd
commit 13c8b341c1
6 changed files with 66 additions and 5 deletions

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@@ -38,6 +38,7 @@
"mobx": "^6.13.7", "mobx": "^6.13.7",
}, },
"devDependencies": { "devDependencies": {
"@effect/language-service": "^0.23.4",
"@eslint/js": "^9.26.0", "@eslint/js": "^9.26.0",
"@tanstack/react-router": "^1.120.3", "@tanstack/react-router": "^1.120.3",
"@tanstack/react-router-devtools": "^1.120.3", "@tanstack/react-router-devtools": "^1.120.3",

View File

@@ -52,7 +52,7 @@ class SubscriptionSubRefImpl<in out A, in out B> extends Effectable.Class<A> imp
readonly setter: (parentValue: B, value: A) => B, readonly setter: (parentValue: B, value: A) => B,
) { ) {
super() super()
this.get = Effect.map(Ref.get(this.parent), this.getter) this.get = Effect.map(this.parent, this.getter)
} }
commit() { commit() {
@@ -86,9 +86,11 @@ class SubscriptionSubRefImpl<in out A, in out B> extends Effectable.Class<A> imp
export const makeFromGetSet = <A, B>( export const makeFromGetSet = <A, B>(
parent: SubscriptionRef.SubscriptionRef<B>, parent: SubscriptionRef.SubscriptionRef<B>,
getter: (parentValue: B) => A, options: {
setter: (parentValue: B, value: A) => B, readonly get: (parentValue: B) => A
): SubscriptionSubRef<A, B> => new SubscriptionSubRefImpl(parent, getter, setter) readonly set: (parentValue: B, value: A) => B
},
): SubscriptionSubRef<A, B> => new SubscriptionSubRefImpl(parent, options.get, options.set)
export const makeFromPath = <B, const P extends PropertyPath.Paths<B>>( export const makeFromPath = <B, const P extends PropertyPath.Paths<B>>(
parent: SubscriptionRef.SubscriptionRef<B>, parent: SubscriptionRef.SubscriptionRef<B>,

View File

@@ -11,6 +11,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"devDependencies": { "devDependencies": {
"@effect/language-service": "^0.23.4",
"@eslint/js": "^9.26.0", "@eslint/js": "^9.26.0",
"@tanstack/react-router": "^1.120.3", "@tanstack/react-router": "^1.120.3",
"@tanstack/react-router-devtools": "^1.120.3", "@tanstack/react-router-devtools": "^1.120.3",

View File

@@ -0,0 +1,48 @@
import { Todo } from "@/domain"
import { KeyValueStore } from "@effect/platform"
import { Chunk, Console, Effect, Option, Schema, Stream, SubscriptionRef } from "effect"
import { SubscriptionSubRef } from "effect-fc/types"
export class TodosState extends Effect.Service<TodosState>()("TodosState", {
effect: Effect.fnUntraced(function*(key: string) {
const kv = yield* KeyValueStore.KeyValueStore
const readFromLocalStorage = Console.log("Reading todos from local storage...").pipe(
Effect.andThen(kv.get(key)),
Effect.flatMap(Option.match({
onSome: Schema.decode(
Schema.parseJson(Schema.Chunk(Todo.TodoFromJson))
),
onNone: () => Effect.succeed(Chunk.empty()),
}))
)
const saveToLocalStorage = (todos: Chunk.Chunk<Todo.Todo>) => Effect.andThen(
Console.log("Saving todos to local storage..."),
Chunk.isNonEmpty(todos)
? Effect.flatMap(
Schema.encode(
Schema.parseJson(Schema.Chunk(Todo.TodoFromJson))
)(todos),
v => kv.set(key, v),
)
: kv.remove(key)
)
const ref = yield* SubscriptionRef.make(yield* readFromLocalStorage)
const makeSubRef = (index: number) => SubscriptionSubRef.makeFromGetSet(ref, {
get: parent => Chunk.unsafeGet(parent, index),
set: (parent, v) => Chunk.replace(parent, index, v),
})
yield* Effect.forkScoped(ref.changes.pipe(
Stream.debounce("500 millis"),
Stream.runForEach(saveToLocalStorage),
))
yield* Effect.addFinalizer(() => Effect.flatMap(ref, saveToLocalStorage))
return { ref, makeSubRef } as const
})
}) {}

View File

@@ -24,7 +24,13 @@
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"]
},
"plugins": [
{
"name": "@effect/language-service"
} }
]
}, },
"include": ["src"] "include": ["src"]
} }