diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 9aeefc7..2d00fb5 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,10 +1,10 @@ import * as AsyncData from "@typed/async-data" -import { Array, Cause, Chunk, type Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, Pipeable, Predicate, pipe, Ref, Schema, type Scope, Stream, type Subscribable, SubscriptionRef } from "effect" +import { Array, Cause, Chunk, type Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, Pipeable, Predicate, Ref, Schema, type Scope, Stream, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import * as Hooks from "./Hooks/index.js" import * as PropertyPath from "./PropertyPath.js" -import * as SubscribableInternal from "./Subscribable.js" +import * as Subscribable from "./Subscribable.js" import * as SubscriptionSubRef from "./SubscriptionSubRef.js" @@ -83,28 +83,14 @@ export const make: { validationFiberRef, submitStateRef, - pipe( - ([value, error, validationFiber, submitState]: readonly [ - Option.Option, - Option.Option, - Option.Option>, - AsyncData.AsyncData, - ]) => Option.isSome(value) && Option.isNone(error) && Option.isNone(validationFiber) && !AsyncData.isLoading(submitState), - - filter => SubscribableInternal.make({ - get: Effect.map(Effect.all([valueRef, errorRef, validationFiberRef, submitStateRef]), filter), - get changes() { - return Stream.map( - Stream.zipLatestAll( - valueRef.changes, - errorRef.changes, - validationFiberRef.changes, - submitStateRef.changes, - ), - filter, - ) - }, - }), + Subscribable.map( + Subscribable.zipLatestAll(valueRef, errorRef, validationFiberRef, submitStateRef), + ([value, error, validationFiber, submitState]) => ( + Option.isSome(value) && + Option.isNone(error) && + Option.isNone(validationFiber) && + !AsyncData.isLoading(submitState) + ), ), ) }) @@ -186,42 +172,20 @@ export const field = , path: P, ): FormField, PropertyPath.ValueFromPath> => new FormFieldImpl( - pipe( - Option.match({ - onSome: (v: A) => Option.map(PropertyPath.get(v, path), Option.some), - onNone: () => Option.some(Option.none()), - }), - filter => SubscribableInternal.make({ - get: Effect.flatMap(self.valueRef, filter), - get changes() { return Stream.flatMap(self.valueRef.changes, filter) }, - }), - ), - + Subscribable.mapEffect(self.valueRef, Option.match({ + onSome: v => Option.map(PropertyPath.get(v, path), Option.some), + onNone: () => Option.some(Option.none()), + })), SubscriptionSubRef.makeFromPath(self.encodedValueRef, path), - - SubscribableInternal.flatMapSubscriptionRef(self.errorRef, Option.match({ + Subscribable.mapEffect(self.errorRef, Option.match({ onSome: flow( ParseResult.ArrayFormatter.formatError, Effect.map(Array.filter(issue => PropertyPath.equivalence(issue.path, path))), ), onNone: () => Effect.succeed([]), })), - - pipe( - Option.isSome, - filter => SubscribableInternal.make({ - get: Effect.map(self.validationFiberRef.get, filter), - get changes() { return Stream.map(self.validationFiberRef.changes, filter) }, - }), - ), - - pipe( - AsyncData.isLoading, - filter => SubscribableInternal.make({ - get: Effect.map(self.submitStateRef, filter), - get changes() { return Stream.map(self.submitStateRef.changes, filter) }, - }), - ), + Subscribable.map(self.validationFiberRef, Option.isSome), + Subscribable.map(self.submitStateRef, AsyncData.isLoading) ) diff --git a/packages/effect-fc/src/Subscribable.ts b/packages/effect-fc/src/Subscribable.ts index 416c6ea..9ebba36 100644 --- a/packages/effect-fc/src/Subscribable.ts +++ b/packages/effect-fc/src/Subscribable.ts @@ -1,32 +1,17 @@ -import { Effect, Effectable, Readable, Stream, Subscribable, type SubscriptionRef } from "effect" +import { Effect, Stream, Subscribable } from "effect" -class SubscribableImpl -extends Effectable.Class implements Subscribable.Subscribable { - readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId - readonly [Subscribable.TypeId]: Subscribable.TypeId = Subscribable.TypeId +export const zipLatestAll = >>( + ...subscribables: T +): Subscribable.Subscribable< + [T[number]] extends [never] + ? never + : { [K in keyof T]: T[K] extends Subscribable.Subscribable ? A : never }, + [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? _E : never, + [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? _R : never +> => Subscribable.make({ + get: Effect.all(subscribables.map(v => v.get)), + changes: Stream.zipLatestAll(...subscribables.map(v => v.changes)), +}) as any - constructor( - readonly get: Effect.Effect, - readonly changes: Stream.Stream, - ) { - super() - } - - commit() { - return this.get - } -} - -export const make = (values: { - readonly get: Effect.Effect - readonly changes: Stream.Stream -}): Subscribable.Subscribable => new SubscribableImpl(values.get, values.changes) - -export const flatMapSubscriptionRef = ( - ref: SubscriptionRef.SubscriptionRef, - flatMap: (value: NoInfer) => Effect.Effect, -): Subscribable.Subscribable => make({ - get: Effect.flatMap(ref, flatMap), - get changes() { return Stream.flatMap(ref.changes, flatMap) }, -}) +export * from "effect/Subscribable"