-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add effect and effect-fn controllers
- Loading branch information
1 parent
5e85697
commit eb4155c
Showing
12 changed files
with
258 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from "./src/effect.controller"; | ||
export * from "./src/stream-effect.controller"; |
93 changes: 93 additions & 0 deletions
93
packages/components/shared-controllers/src/effect-fn.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { | ||
getOrCreateRuntime, | ||
type EchoRuntimeServices, | ||
} from "@echo/services-bootstrap-runtime"; | ||
import { Effect } from "effect"; | ||
import type { ReactiveController, ReactiveControllerHost } from "lit"; | ||
import type { StatusListener } from "./shared.interface"; | ||
|
||
type StreamStatus<A, E> = | ||
| { _tag: "Initial" } | ||
| { _tag: "Pending" } | ||
| { _tag: "Complete"; result: A } | ||
| { _tag: "Error"; error: E }; | ||
|
||
/** | ||
* Controller that takes a function that produces an effect and exposes a | ||
* method to execute the effect and render the different states of the effect. | ||
*/ | ||
export class EffectFnController<P, A, E> implements ReactiveController { | ||
private host: ReactiveControllerHost; | ||
private _status: StreamStatus<A, E> = { _tag: "Initial" }; | ||
|
||
constructor( | ||
host: ReactiveControllerHost, | ||
private readonly _effectFn: ( | ||
p: P, | ||
) => Effect.Effect<A, E, EchoRuntimeServices>, | ||
/** | ||
* Optional listeners that will be called when the effect produces a value | ||
* or errors. Only meant to be used by hosts that require side effects | ||
* when the effect produces a value or errors, otherwise use the render | ||
* method to render the different states of the effect. | ||
*/ | ||
private readonly _listeners?: Omit<StatusListener<A, E>, "initial">, | ||
) { | ||
(this.host = host).addController(this); | ||
} | ||
|
||
hostConnected(): void {} | ||
|
||
/** | ||
* Runs the effect with the given parameters. This produces a value or an error | ||
* that gets notified to the host and triggers the listeners if they are provided. | ||
*/ | ||
run(params: P) { | ||
const consumer = this._effectFn(params).pipe( | ||
Effect.tap((result) => | ||
Effect.sync(() => this.handleUpdate$({ _tag: "Complete", result })), | ||
), | ||
Effect.tapError((error) => | ||
Effect.sync(() => this.handleUpdate$({ _tag: "Error", error })), | ||
), | ||
); | ||
|
||
this.handleUpdate$({ | ||
_tag: "Pending", | ||
}); | ||
getOrCreateRuntime().runPromise(consumer); | ||
} | ||
|
||
/** | ||
* Maps the different states of the effect to a renderer. | ||
*/ | ||
render(renderer: StatusListener<A, E>) { | ||
switch (this._status._tag) { | ||
case "Initial": | ||
return renderer.initial?.(); | ||
case "Pending": | ||
return renderer.pending?.(); | ||
case "Complete": | ||
return renderer.complete?.(this._status.result); | ||
case "Error": | ||
return renderer.error?.(this._status.error); | ||
} | ||
} | ||
|
||
private handleUpdate$(state: StreamStatus<A, E>) { | ||
switch (state._tag) { | ||
case "Pending": | ||
this._listeners?.pending?.(); | ||
break; | ||
case "Complete": | ||
this._listeners?.complete?.(state.result); | ||
break; | ||
case "Error": | ||
this._listeners?.error?.(state.error); | ||
break; | ||
} | ||
|
||
this._status = state; | ||
this.host.requestUpdate(); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
packages/components/shared-controllers/src/effect.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { type EchoRuntimeServices } from "@echo/services-bootstrap-runtime"; | ||
import { Effect } from "effect"; | ||
import type { ReactiveControllerHost } from "lit"; | ||
import type { StatusListener } from "./shared.interface"; | ||
import { EffectFnController } from "./effect-fn.controller"; | ||
|
||
/** | ||
* Controller that takes an effect that can be executed by the default runtime | ||
* of the application, and exposes a render method that maps each different | ||
* status of the effect to a renderer. This is meant to be used with effects | ||
* that are one-shot, meaning they only produce a single value. For multiple | ||
* values, use the StreamEffectController, which can handle streams and | ||
* subscription refs. | ||
*/ | ||
export class EffectController<A, E> extends EffectFnController<void, A, E> { | ||
constructor( | ||
host: ReactiveControllerHost, | ||
_effect: Effect.Effect<A, E, EchoRuntimeServices>, | ||
/** | ||
* Optional listeners that will be called when the effect produces a value | ||
* or errors. Only meant to be used by hosts that require side effects | ||
* when the effect produces a value or errors, otherwise use the render | ||
* method to render the different states of the effect. | ||
*/ | ||
_listeners?: Omit<StatusListener<A, E>, "initial">, | ||
) { | ||
super(host, () => _effect, _listeners); | ||
} | ||
|
||
hostConnected(): void { | ||
this.run(); | ||
} | ||
} |
Oops, something went wrong.