diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..55712c1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/bun.lock b/bun.lock index 9afafdf..ec0cbc9 100644 --- a/bun.lock +++ b/bun.lock @@ -38,6 +38,7 @@ "mobx": "^6.13.7", }, "devDependencies": { + "@effect/language-service": "^0.23.4", "@eslint/js": "^9.26.0", "@tanstack/react-router": "^1.120.3", "@tanstack/react-router-devtools": "^1.120.3", diff --git a/packages/effect-fc/src/types/SubscriptionSubRef.ts b/packages/effect-fc/src/types/SubscriptionSubRef.ts index b32f45d..0f2db0d 100644 --- a/packages/effect-fc/src/types/SubscriptionSubRef.ts +++ b/packages/effect-fc/src/types/SubscriptionSubRef.ts @@ -52,7 +52,7 @@ class SubscriptionSubRefImpl extends Effectable.Class imp readonly setter: (parentValue: B, value: A) => B, ) { super() - this.get = Effect.map(Ref.get(this.parent), this.getter) + this.get = Effect.map(this.parent, this.getter) } commit() { @@ -86,9 +86,11 @@ class SubscriptionSubRefImpl extends Effectable.Class imp export const makeFromGetSet = ( parent: SubscriptionRef.SubscriptionRef, - getter: (parentValue: B) => A, - setter: (parentValue: B, value: A) => B, -): SubscriptionSubRef => new SubscriptionSubRefImpl(parent, getter, setter) + options: { + readonly get: (parentValue: B) => A + readonly set: (parentValue: B, value: A) => B + }, +): SubscriptionSubRef => new SubscriptionSubRefImpl(parent, options.get, options.set) export const makeFromPath = >( parent: SubscriptionRef.SubscriptionRef, diff --git a/packages/example/package.json b/packages/example/package.json index 36aa910..2d2e941 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -11,6 +11,7 @@ "preview": "vite preview" }, "devDependencies": { + "@effect/language-service": "^0.23.4", "@eslint/js": "^9.26.0", "@tanstack/react-router": "^1.120.3", "@tanstack/react-router-devtools": "^1.120.3", diff --git a/packages/example/src/todo/TodosState.ts b/packages/example/src/todo/TodosState.ts new file mode 100644 index 0000000..f8bb8c2 --- /dev/null +++ b/packages/example/src/todo/TodosState.ts @@ -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", { + 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) => 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 + }) +}) {} diff --git a/packages/example/tsconfig.app.json b/packages/example/tsconfig.app.json index 0592374..61b3094 100644 --- a/packages/example/tsconfig.app.json +++ b/packages/example/tsconfig.app.json @@ -24,7 +24,13 @@ "paths": { "@/*": ["./src/*"] - } + }, + + "plugins": [ + { + "name": "@effect/language-service" + } + ] }, "include": ["src"] }