From 606f5ec5efafdba6b0861b96cbd367e48175fe5a Mon Sep 17 00:00:00 2001 From: Tylor Steinberger Date: Sat, 23 Dec 2023 12:33:06 -0500 Subject: [PATCH] feat: update navigation to fx v3 --- packages/fx/src/Fx.ts | 25 +++++--- packages/fx/src/RefSubject.ts | 9 ++- packages/fx/src/internal/effect-operator.ts | 4 +- packages/fx/test/core.ts | 18 ++++++ packages/navigation/src/Blocking.ts | 24 +++---- packages/navigation/src/Navigation.ts | 32 +++++----- .../navigation/src/internal/fromWindow.ts | 62 ++++++------------- packages/navigation/src/internal/memory.ts | 19 ++++-- packages/navigation/src/internal/shared.ts | 29 +++++---- packages/navigation/test/Navigation.ts | 41 ++++++------ 10 files changed, 137 insertions(+), 126 deletions(-) diff --git a/packages/fx/src/Fx.ts b/packages/fx/src/Fx.ts index 05ba240e1..4de537891 100644 --- a/packages/fx/src/Fx.ts +++ b/packages/fx/src/Fx.ts @@ -21,16 +21,16 @@ import { Cause, Effect, Either, Layer, Option, Queue, Schedule } from "effect" import type { DurationInput } from "effect/Duration" import { dual, identity } from "effect/Function" import type * as Types from "effect/Types" -import * as strategies from "./internal/strategies.js" -import { TypeId } from "./TypeId.js" import * as Emitter from "./Emitter.js" import * as core from "./internal/core.js" import * as coreKeyed from "./internal/keyed.js" import * as coreShare from "./internal/share.js" +import * as strategies from "./internal/strategies.js" import * as coreWithKey from "./internal/withKey.js" import { type RefSubject, transform } from "./RefSubject.js" import * as Sink from "./Sink.js" import type * as Subject from "./Subject.js" +import { TypeId } from "./TypeId.js" export interface Fx extends Pipeable.Pipeable { readonly [TypeId]: Fx.Variance @@ -596,13 +596,13 @@ export const continueWith: { } = dual(2, core.continueWith) export const orElseCause: { - (f: (cause: Cause.Cause) => Fx): (fx: Fx) => Fx - (fx: Fx, f: (cause: Cause.Cause) => Fx): Fx + (f: (cause: Cause.Cause) => Fx): (fx: Fx) => Fx + (fx: Fx, f: (cause: Cause.Cause) => Fx): Fx } = dual(2, core.orElseCause) export const orElse: { - (f: (error: E) => Fx): (fx: Fx) => Fx - (fx: Fx, f: (error: E) => Fx): Fx + (f: (error: E) => Fx): (fx: Fx) => Fx + (fx: Fx, f: (error: E) => Fx): Fx } = dual(2, core.orElse) export const suspend: (f: () => Fx) => Fx = core.suspend @@ -927,8 +927,8 @@ export const multicast: (fx: Fx) => Fx export const hold: (fx: Fx) => Fx = coreShare.hold export const replay: { - (capacity: number): (fx: Fx) => Fx - (fx: Fx, capacity: number): Fx + (capacity: number): (fx: Fx) => Fx + (fx: Fx, capacity: number): Fx } = dual(2, coreShare.replay) export const mapCause: { @@ -1660,8 +1660,13 @@ export const fromAsyncIterable: (iterable: AsyncIterable) => Fx(f: (a: A) => Either.Either): (fx: Fx) => readonly [Fx, Fx] - (fx: Fx, f: (a: A) => Either.Either): readonly [Fx, Fx] + ( + f: (a: A) => Either.Either + ): (fx: Fx) => readonly [Fx, Fx] + ( + fx: Fx, + f: (a: A) => Either.Either + ): readonly [Fx, Fx] } = dual(2, function partitionMap( fx: Fx, f: (a: A) => Either.Either diff --git a/packages/fx/src/RefSubject.ts b/packages/fx/src/RefSubject.ts index 3bd1b2d8e..8c9736cfe 100644 --- a/packages/fx/src/RefSubject.ts +++ b/packages/fx/src/RefSubject.ts @@ -16,7 +16,6 @@ import { import * as Boolean from "effect/Boolean" import { dual } from "effect/Function" import { sum } from "effect/Number" -import { ComputedTypeId, FilteredTypeId, RefSubjectTypeId, TypeId } from "./TypeId.js" import type { Fx } from "./Fx.js" import * as core from "./internal/core.js" import * as DeferredRef from "./internal/DeferredRef.js" @@ -27,6 +26,7 @@ import * as share from "./internal/share.js" import type { UnionToTuple } from "./internal/UnionToTuple.js" import * as Sink from "./Sink.js" import * as Subject from "./Subject.js" +import { ComputedTypeId, FilteredTypeId, RefSubjectTypeId, TypeId } from "./TypeId.js" import * as Versioned from "./Versioned.js" const UNBOUNDED = { concurrency: "unbounded" } as const @@ -112,8 +112,11 @@ export function fromFx( Effect.tap(({ core, deferredRef }) => Effect.forkIn( fx.run(Sink.make( - (cause) => Effect.flatMap(Effect.sync(() => deferredRef.done(Exit.failCause(cause))), () => core.subject.onFailure(cause)), - (value) => Effect.flatMap(Effect.sync(() => deferredRef.done(Exit.succeed(value))), () => setCore(core, value)) + (cause) => + Effect.flatMap(Effect.sync(() => deferredRef.done(Exit.failCause(cause))), () => + core.subject.onFailure(cause)), + (value) => + Effect.flatMap(Effect.sync(() => deferredRef.done(Exit.succeed(value))), () => setCore(core, value)) )), core.scope ) diff --git a/packages/fx/src/internal/effect-operator.ts b/packages/fx/src/internal/effect-operator.ts index cca2c09ce..ca399ab36 100644 --- a/packages/fx/src/internal/effect-operator.ts +++ b/packages/fx/src/internal/effect-operator.ts @@ -64,7 +64,7 @@ type EffectOperatorFusionMap = { const EffectOperatorFusionMap: EffectOperatorFusionMap = { MapEffect: { MapEffect: (op1, op2) => MapEffect((a: any) => Effect.flatMap(op1.f(a), op2.f)), - TapEffect: (op1, op2) => TapEffect((a: any) => Effect.tap(op1.f(a), op2.f)), + TapEffect: (op1, op2) => MapEffect((a: any) => Effect.tap(op1.f(a), op2.f)), FilterEffect: (op1, op2) => FilterMapEffect((a: any) => Effect.flatMap( @@ -123,7 +123,7 @@ const EffectOperatorFusionMap: EffectOperatorFusionMap = { op1.f(a), Option.match({ onNone: () => Effect.succeedNone, - onSome: (b) => op2.f(a).pipe(Effect.map(() => b)) + onSome: (b) => Effect.as(op2.f(b), Option.some(b)) }) ) ), diff --git a/packages/fx/test/core.ts b/packages/fx/test/core.ts index acce69ea2..367a32479 100644 --- a/packages/fx/test/core.ts +++ b/packages/fx/test/core.ts @@ -1065,4 +1065,22 @@ describe.concurrent("Fx.partitionMap", () => { expect(odds).toEqual([1, 3]) expect(evens).toEqual([2, 4]) }) + + describe("fusion", () => { + describe("filterMap + tapEffect", () => { + it("works as expected", async () => { + const values: Array = [] + const test = Fx.fromIterable([1, 2, 3]).pipe( + Fx.filterMap(Option.liftPredicate((x) => x % 2 === 0)), + Fx.tapEffect((x) => Effect.sync(() => values.push(x))), + Fx.toReadonlyArray + ) + + const array = await Effect.runPromise(test) + + expect(array).toEqual([2]) + expect(values).toEqual([2]) + }) + }) + }) }) diff --git a/packages/navigation/src/Blocking.ts b/packages/navigation/src/Blocking.ts index 0192d8570..d85554827 100644 --- a/packages/navigation/src/Blocking.ts +++ b/packages/navigation/src/Blocking.ts @@ -2,10 +2,9 @@ * @since 1.0.0 */ -import type * as Computed from "@typed/fx/Computed" import * as RefSubject from "@typed/fx/RefSubject" import type { Scope } from "effect" -import { Deferred, Effect, Option } from "effect" +import { Data, Deferred, Effect, Option } from "effect" import type { BeforeNavigationEvent, CancelNavigation, @@ -18,8 +17,8 @@ import { cancelNavigation, Navigation, redirectToPath } from "./Navigation.js" /** * @since 1.0.0 */ -export interface BlockNavigation extends Computed.Computed> { - readonly isBlocking: Computed.Computed +export interface BlockNavigation extends RefSubject.Computed> { + readonly isBlocking: RefSubject.Computed } /** @@ -36,7 +35,7 @@ type InternalBlockState = Unblocked | Blocked type Unblocked = { readonly _tag: "Unblocked" } -const Unblocked: Unblocked = { _tag: "Unblocked" } +const Unblocked: Unblocked = Data.struct({ _tag: "Unblocked" }) type Blocked = { readonly _tag: "Blocked" @@ -47,7 +46,7 @@ type Blocked = { const Blocked = (event: BeforeNavigationEvent) => Effect.map( Deferred.make(), - (deferred): Blocked => ({ _tag: "Blocked", deferred, event }) + (deferred): Blocked => Data.struct({ _tag: "Blocked", deferred, event }) ) /** @@ -69,7 +68,7 @@ export const useBlockNavigation = ( yield* _( navigation.beforeNavigation((event) => - blockState.modifyEffect((state) => + RefSubject.modifyEffect(blockState, (state) => Effect.gen(function*(_) { // Can't block twice if (state._tag === "Blocked") return [Option.none(), state] as const @@ -84,14 +83,17 @@ export const useBlockNavigation = ( Option.some(Deferred.await(updated.deferred)), updated ] as const - }) - ) + })) ) ) const blockNavigation: BlockNavigation = Object.assign( - blockState.map((s) => s._tag === "Blocked" ? Option.some(blockedToBlocking(navigation, s)) : Option.none()), - { isBlocking: blockState.map((s) => s._tag === "Blocked") } + RefSubject.map(blockState, (s) => { + return s._tag === "Blocked" ? Option.some(blockedToBlocking(navigation, s)) : Option.none() + }), + { + isBlocking: RefSubject.map(blockState, (s) => s._tag === "Blocked") + } ) return blockNavigation diff --git a/packages/navigation/src/Navigation.ts b/packages/navigation/src/Navigation.ts index 1b8593440..5ede7a5c8 100644 --- a/packages/navigation/src/Navigation.ts +++ b/packages/navigation/src/Navigation.ts @@ -5,7 +5,7 @@ import { ParseResult } from "@effect/schema" import * as Schema from "@effect/schema/Schema" import { Tagged } from "@typed/context" -import * as Computed from "@typed/fx/Computed" +import * as RefSubject from "@typed/fx/RefSubject" import type { Uuid } from "@typed/id" import * as IdSchema from "@typed/id/Schema" import type { Option, Scope } from "effect" @@ -19,15 +19,15 @@ export interface Navigation { readonly base: string - readonly currentEntry: Computed.Computed + readonly currentEntry: RefSubject.Computed - readonly entries: Computed.Computed> + readonly entries: RefSubject.Computed> - readonly transition: Computed.Computed> + readonly transition: RefSubject.Computed> - readonly canGoBack: Computed.Computed + readonly canGoBack: RefSubject.Computed - readonly canGoForward: Computed.Computed + readonly canGoForward: RefSubject.Computed readonly navigate: ( url: string | URL, @@ -324,7 +324,7 @@ export const reload: ( /** * @since 1.0.0 */ -export const CurrentEntry: Computed.Computed = Computed.fromTag( +export const CurrentEntry: RefSubject.Computed = RefSubject.computedFromTag( Navigation, (nav) => nav.currentEntry ) @@ -339,22 +339,24 @@ export function getCurrentPathFromUrl(location: Pick = CurrentEntry.map((d) => - getCurrentPathFromUrl(d.url) +export const CurrentPath: RefSubject.Computed = RefSubject.map( + CurrentEntry, + (d) => getCurrentPathFromUrl(d.url) ) /** * @since 1.0.0 */ -export const CurrentEntries: Computed.Computed> = Computed.fromTag( - Navigation, - (n) => n.entries -) +export const CurrentEntries: RefSubject.Computed> = RefSubject + .computedFromTag( + Navigation, + (n) => n.entries + ) /** * @since 1.0.0 */ -export const CanGoForward: Computed.Computed = Computed.fromTag( +export const CanGoForward: RefSubject.Computed = RefSubject.computedFromTag( Navigation, (n) => n.canGoForward ) @@ -362,7 +364,7 @@ export const CanGoForward: Computed.Computed = Compu /** * @since 1.0.0 */ -export const CanGoBack: Computed.Computed = Computed.fromTag( +export const CanGoBack: RefSubject.Computed = RefSubject.computedFromTag( Navigation, (n) => n.canGoBack ) diff --git a/packages/navigation/src/internal/fromWindow.ts b/packages/navigation/src/internal/fromWindow.ts index 9146863b3..324ddf297 100644 --- a/packages/navigation/src/internal/fromWindow.ts +++ b/packages/navigation/src/internal/fromWindow.ts @@ -1,27 +1,21 @@ +import * as Equivalence from "@effect/schema/Equivalence" import { unsafeGet } from "@typed/context" import { Window } from "@typed/dom/Window" -import type { Computed } from "@typed/fx/Computed" import * as RefSubject from "@typed/fx/RefSubject" import { GetRandomValues, Uuid } from "@typed/id" import { Effect, ExecutionStrategy, Exit, Fiber, Option, Runtime, Scope } from "effect" -import type { Context, Layer } from "effect" +import type { Layer } from "effect" import type { Commit } from "../Layer.js" -import type { - BeforeNavigationEvent, - BeforeNavigationHandler, - Destination, - NavigationEvent, - NavigationHandler, - Transition -} from "../Navigation.js" +import type { BeforeNavigationEvent, Destination, NavigationEvent, Transition } from "../Navigation.js" import { Navigation, NavigationError } from "../Navigation.js" -import type { NavigationState } from "./shared.js" +import type { ModelAndIntent } from "./shared.js" import { getOriginalState, getUrl, isPatchedState, makeDestination, makeHandlersState, + NavigationState, setupFromModelAndIntent } from "./shared.js" @@ -94,31 +88,6 @@ function getBaseHref(window: Window) { return base ? base.href : "/" } -type ModelAndIntent = { - readonly state: RefSubject.RefSubject - readonly canGoBack: Computed< - never, - never, - boolean - > - readonly canGoForward: Computed< - never, - never, - boolean - > - readonly beforeHandlers: RefSubject.RefSubject< - never, - never, - Set, Context.Context]> - > - readonly handlers: RefSubject.RefSubject< - never, - never, - Set, Context.Context]> - > - readonly commit: Commit -} - const getNavigationState = (navigation: NativeNavigation): NavigationState => { const entries = navigation.entries().map(nativeEntryToDestination) const { index } = navigation.currentEntry @@ -141,11 +110,12 @@ function setupWithNavigation( return Effect.gen(function*(_) { const state = yield* _( RefSubject.fromEffect( - Effect.sync((): NavigationState => getNavigationState(navigation)) + Effect.sync((): NavigationState => getNavigationState(navigation)), + { eq: Equivalence.to(NavigationState) } ) ) - const canGoBack = state.map((s) => s.index > 0) - const canGoForward = state.map((s) => s.index < s.entries.length - 1) + const canGoBack = RefSubject.map(state, (s) => s.index > 0) + const canGoForward = RefSubject.map(state, (s) => s.index < s.entries.length - 1) const { beforeHandlers, handlers } = yield* _(makeHandlersState()) const commit: Commit = (to: Destination, event: BeforeNavigationEvent) => Effect.gen(function*(_) { @@ -264,11 +234,12 @@ function setupWithHistory( ), (destination): NavigationState => ({ entries: [destination], index: 0, transition: Option.none() }) ) - ) + ), + { eq: Equivalence.to(NavigationState) } ) ) - const canGoBack = state.map((s) => s.index > 0) - const canGoForward = state.map((s) => s.index < s.entries.length - 1) + const canGoBack = RefSubject.map(state, (s) => s.index > 0) + const canGoForward = RefSubject.map(state, (s) => s.index < s.entries.length - 1) const { beforeHandlers, handlers } = yield* _(makeHandlersState()) const commit: Commit = ({ id, key, state, url }: Destination, event: BeforeNavigationEvent) => Effect.sync(() => { @@ -292,7 +263,7 @@ function setupWithHistory( beforeHandlers, handlers, commit - } as const + } as ModelAndIntent }) } @@ -445,7 +416,10 @@ function scopedRuntime(): Effect.Effect< childScope, Effect.suspend(() => fiber ? Fiber.interrupt(fiber) : Effect.unit) ), - Effect.onExit(effect, (exit) => Scope.close(childScope, exit)) + Effect.onExit( + effect, + (exit) => Scope.close(childScope, exit) + ) ) ), runFork diff --git a/packages/navigation/src/internal/memory.ts b/packages/navigation/src/internal/memory.ts index 2f46ac910..44760b004 100644 --- a/packages/navigation/src/internal/memory.ts +++ b/packages/navigation/src/internal/memory.ts @@ -1,11 +1,19 @@ +import * as Equivalence from "@effect/schema/Equivalence" import * as RefSubject from "@typed/fx/RefSubject" import { GetRandomValues, getRandomValues } from "@typed/id" import { Effect, Option } from "effect" import type { Layer, Scope } from "effect" import type { Commit, InitialMemoryOptions, MemoryOptions } from "../Layer.js" import { Navigation } from "../Navigation.js" -import type { ModelAndIntent, NavigationState } from "./shared.js" -import { getOriginFromUrl, getUrl, makeDestination, makeHandlersState, setupFromModelAndIntent } from "./shared.js" +import type { ModelAndIntent } from "./shared.js" +import { + getOriginFromUrl, + getUrl, + makeDestination, + makeHandlersState, + NavigationState, + setupFromModelAndIntent +} from "./shared.js" export const memory = (options: MemoryOptions): Layer.Layer => Navigation.scoped( @@ -59,11 +67,12 @@ function setupMemory( index: options.currentIndex ?? options.entries.length - 1, transition: Option.none() } - }) + }), + { eq: Equivalence.to(NavigationState) } ) ) - const canGoBack = state.map((s) => s.index > 0) - const canGoForward = state.map((s) => s.index < s.entries.length - 1) + const canGoBack = RefSubject.map(state, (s) => s.index > 0) + const canGoForward = RefSubject.map(state, (s) => s.index < s.entries.length - 1) const { beforeHandlers, handlers } = yield* _(makeHandlersState()) const commit: Commit = options.commit ?? (() => Effect.unit) diff --git a/packages/navigation/src/internal/shared.ts b/packages/navigation/src/internal/shared.ts index 931efbb1f..b2a7a0c27 100644 --- a/packages/navigation/src/internal/shared.ts +++ b/packages/navigation/src/internal/shared.ts @@ -1,6 +1,5 @@ import { Schema } from "@effect/schema" import type * as Context from "@typed/context" -import type { Computed } from "@typed/fx/Computed" import * as RefSubject from "@typed/fx/RefSubject" import type { Uuid } from "@typed/id" import { GetRandomValues, makeUuid } from "@typed/id" @@ -39,12 +38,12 @@ export const getUrl = (origin: string, urlOrPath: string | URL): URL => { export type ModelAndIntent = { readonly state: RefSubject.RefSubject - readonly canGoBack: Computed< + readonly canGoBack: RefSubject.Computed< never, never, boolean > - readonly canGoForward: Computed< + readonly canGoForward: RefSubject.Computed< never, never, boolean @@ -71,9 +70,9 @@ export function setupFromModelAndIntent( ) { const { beforeHandlers, canGoBack, canGoForward, commit, handlers, state } = modelAndIntent - const entries = state.map((s) => s.entries) - const currentEntry = state.map((s) => s.entries[s.index]) - const transition = state.map((s) => s.transition) + const entries = RefSubject.map(state, (s) => s.entries) + const currentEntry = RefSubject.map(state, (s) => s.entries[s.index]) + const transition = RefSubject.map(state, (s) => s.transition) const runBeforeHandlers = (event: BeforeNavigationEvent) => Effect.gen(function*(_) { @@ -117,7 +116,7 @@ export function setupFromModelAndIntent( } if (matches.length > 0) { - yield* _(Effect.all(matches)) + yield* _(Effect.all(matches, { discard: true })) } }) @@ -210,7 +209,7 @@ export function setupFromModelAndIntent( }) const navigate = (pathOrUrl: string | URL, options?: NavigateOptions, skipCommit: boolean = false) => - state.runUpdate((get, set) => + state.runUpdates(({ get, set }) => Effect.gen(function*(_) { const state = yield* _(get) const from = state.entries[state.index] @@ -233,7 +232,7 @@ export function setupFromModelAndIntent( ) const traverseTo = (key: Destination["key"], options?: { readonly info?: unknown }, skipCommit: boolean = false) => - state.runUpdate((get, set) => + state.runUpdates(({ get, set }) => Effect.gen(function*(_) { const state = yield* _(get) const { entries, index } = state @@ -276,7 +275,7 @@ export function setupFromModelAndIntent( }) const reload = (options?: { readonly info?: unknown }, skipCommit: boolean = false) => - state.runUpdate((get, set) => + state.runUpdates(({ get, set }) => Effect.gen(function*(_) { const { entries, index } = yield* _(state) const current = entries[index] @@ -300,9 +299,9 @@ export function setupFromModelAndIntent( const entry = [handler, ctx] as const return Effect.zipRight( - beforeHandlers.update((handlers) => new Set([...handlers, entry])), + RefSubject.update(beforeHandlers, (handlers) => new Set([...handlers, entry])), Effect.addFinalizer(() => - beforeHandlers.update((handlers) => { + RefSubject.update(beforeHandlers, (handlers) => { const updated = new Set(handlers) updated.delete(entry) return updated @@ -318,9 +317,9 @@ export function setupFromModelAndIntent( const entry = [handler, ctx] as const return Effect.zipRight( - handlers.update((handlers) => new Set([...handlers, entry])), + RefSubject.update(handlers, (handlers) => new Set([...handlers, entry])), Effect.addFinalizer(() => - handlers.update((handlers) => { + RefSubject.update(handlers, (handlers) => { const updated = new Set(handlers) updated.delete(entry) return updated @@ -330,7 +329,7 @@ export function setupFromModelAndIntent( }) const updateCurrentEntry = (options: { readonly state: unknown }) => - state.runUpdate((get, set) => + state.runUpdates(({ get, set }) => Effect.gen(function*(_) { const { entries, index } = yield* _(get) const current = entries[index] diff --git a/packages/navigation/test/Navigation.ts b/packages/navigation/test/Navigation.ts index c6afacad0..4d49cff0c 100644 --- a/packages/navigation/test/Navigation.ts +++ b/packages/navigation/test/Navigation.ts @@ -43,8 +43,8 @@ describe(__filename, () => { const count = yield* _(RefSubject.of(0)) - yield* _(beforeNavigation(() => Effect.succeedSome(count.update((x) => x + 10)))) - yield* _(onNavigation(() => Effect.succeedSome(count.update((x) => x * 2)))) + yield* _(beforeNavigation(() => Effect.succeedSome(RefSubject.update(count, (x) => x + 10)))) + yield* _(onNavigation(() => Effect.succeedSome(RefSubject.update(count, (x) => x * 2)))) const second = yield* _(navigate("/foo/2")) @@ -103,8 +103,8 @@ describe(__filename, () => { const count = yield* _(RefSubject.of(0)) - yield* _(beforeNavigation(() => Effect.succeedSome(count.update((x) => x + 10)))) - yield* _(onNavigation(() => Effect.succeedSome(count.update((x) => x * 2)))) + yield* _(beforeNavigation(() => Effect.succeedSome(RefSubject.update(count, (x) => x + 10)))) + yield* _(onNavigation(() => Effect.succeedSome(RefSubject.update(count, (x) => x * 2)))) const second = yield* _(navigate("/foo/2")) @@ -437,8 +437,8 @@ describe(__filename, () => { const count = yield* _(RefSubject.of(0)) - yield* _(beforeNavigation(() => Effect.succeedSome(count.update((x) => x + 10)))) - yield* _(onNavigation(() => Effect.succeedSome(count.update((x) => x * 2)))) + yield* _(beforeNavigation(() => Effect.succeedSome(RefSubject.update(count, (x) => x + 10)))) + yield* _(onNavigation(() => Effect.succeedSome(RefSubject.update(count, (x) => x * 2)))) const second = yield* _(navigate("/foo/2")) @@ -489,19 +489,20 @@ describe(__filename, () => { yield* _( blockNavigation, - Fx.tap((option) => { - if (Option.isNone(option)) { - return Effect.unit - } else { - const blocking = option.value - didBlock = true - return blocking.confirm - } + Fx.compact, + Fx.tapEffect((blocking) => { + didBlock = true + return blocking.confirm }), Fx.forkScoped ) - yield* _(Navigation.navigate(nextUrl)) + // Let fiber start + yield* _(Effect.sleep(1)) + + const result = yield* _(Navigation.navigate(nextUrl), Effect.either) + + console.log("result", result) deepStrictEqual(didBlock, true) @@ -518,15 +519,13 @@ describe(__filename, () => { yield* _( blockNavigation, - Fx.tap((option) => { - if (Option.isNone(option)) { - return Effect.unit - } else { - const blocking = option.value + Fx.tapEffect(Option.match({ + onNone: () => Effect.unit, + onSome: (blocking) => { didBlock = true return blocking.cancel } - }), + })), Fx.forkScoped )