diff --git a/packages/effect-fc/src/SynchronizedForm.ts b/packages/effect-fc/src/SynchronizedForm.ts index ebf83b2..93cc40d 100644 --- a/packages/effect-fc/src/SynchronizedForm.ts +++ b/packages/effect-fc/src/SynchronizedForm.ts @@ -39,8 +39,11 @@ export class SynchronizedFormImpl< readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId = SynchronizedFormTypeId readonly path = [] as const + readonly value: Subscribable.Subscribable, never, never> readonly encodedValue: Lens.Lens + readonly isValidating: Subscribable.Subscribable + readonly canCommit: Subscribable.Subscribable constructor( readonly schema: Schema.Schema, @@ -50,17 +53,66 @@ export class SynchronizedFormImpl< readonly internalEncodedValue: Lens.Lens, readonly issues: Lens.Lens, readonly validationFiber: Lens.Lens>, never, never, never, never>, - readonly isValidating: Subscribable.Subscribable, - - readonly canCommit: Subscribable.Subscribable, readonly isCommitting: Lens.Lens, readonly runSemaphore: Effect.Semaphore, ) { super() - this.value = makeValueSubscribable(this) - this.encodedValue = makeEncodedValueLens(this) + this.value = Effect.succeed(this).pipe( + Effect.map(self => Subscribable.make({ + get get() { return Effect.provide(Effect.option(self.target.get), self.context) }, + get changes() { + return Stream.provideContext( + self.target.changes.pipe( + Stream.map(Option.some), + Stream.catchAll(() => Stream.make(Option.none())), + ), + self.context, + ) + }, + })), + Subscribable.unwrap, + ) + this.encodedValue = Effect.succeed(this).pipe( + Effect.map(self => Lens.make({ + get get() { return self.internalEncodedValue.get }, + get changes() { return self.internalEncodedValue.changes }, + modify: f => self.internalEncodedValue.modify( + encodedValue => Effect.map( + f(encodedValue), + ([b, nextEncodedValue]) => [ + [b, nextEncodedValue] as const, + nextEncodedValue, + ] as const + ) + ).pipe( + Effect.tap(([, nextEncodedValue]) => + self.synchronizeEncodedValue(nextEncodedValue).pipe( + Effect.forkScoped, + Effect.provide(self.context), + ) + ), + Effect.map(([b]) => b), + ), + })), + Lens.unwrap, + ) + this.isValidating = Effect.succeed(this).pipe( + Effect.map(self => Subscribable.map(self.validationFiber, Option.isSome)), + Subscribable.unwrap, + ) + this.canCommit = Effect.succeed(this).pipe( + Effect.map(self => Subscribable.map( + Subscribable.zipLatestAll(self.issues, self.validationFiber, self.isCommitting), + ([issues, validationFiber, isCommitting]) => ( + Array.isEmptyReadonlyArray(issues) && + Option.isNone(validationFiber) && + !isCommitting + ), + )), + Subscribable.unwrap, + ) } synchronizeEncodedValue(encodedValue: I): Effect.Effect { @@ -139,37 +191,6 @@ export class SynchronizedFormImpl< } } -const makeValueSubscribable = ( - self: SynchronizedFormImpl -): Subscribable.Subscribable, never, never> => Subscribable.make({ - get get() { return Effect.provide(Effect.option(self.target.get), self.context) }, - get changes() { return Stream.provideContext(self.target.changes, self.context) } -}) - -const makeEncodedValueLens = ( - self: SynchronizedFormImpl -): Lens.Lens => Lens.make({ - get get() { return self.internalEncodedValue.get }, - get changes() { return self.internalEncodedValue.changes }, - modify: f => self.internalEncodedValue.modify( - encodedValue => Effect.map( - f(encodedValue), - ([b, nextEncodedValue]) => [ - [b, nextEncodedValue] as const, - nextEncodedValue, - ] as const - ) - ).pipe( - Effect.tap(([, nextEncodedValue]) => - self.synchronizeEncodedValue(nextEncodedValue).pipe( - Effect.forkScoped, - Effect.provide(self.context), - ) - ), - Effect.map(([b]) => b), - ), -}) - export const isSynchronizedForm = (u: unknown): u is SynchronizedForm => Predicate.hasProperty(u, SynchronizedFormTypeId) @@ -196,30 +217,15 @@ export const make = Effect.fnUntraced(function* (Array.empty())) - const validationFiberLens = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())) - const isCommittingLens = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(false)) - return new SynchronizedFormImpl( options.schema, yield* Effect.context(), options.target, - internalEncodedValueLens, - issuesLens, - validationFiberLens, - Subscribable.map(validationFiberLens, Option.isSome), - - Subscribable.map( - Subscribable.zipLatestAll(issuesLens, validationFiberLens, isCommittingLens), - ([issues, validationFiber, isCommitting]) => ( - Array.isEmptyReadonlyArray(issues) && - Option.isNone(validationFiber) && - !isCommitting - ), - ), - isCommittingLens, + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(initialEncodedValue)), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Array.empty())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(false)), yield* Effect.makeSemaphore(1), )