From 9fae6a3d76d17bd031325a6073e179217da12e57 Mon Sep 17 00:00:00 2001 From: sleepyfran Date: Sat, 27 Jul 2024 15:08:28 +0200 Subject: [PATCH] Initial player svc implementation --- package.json | 2 +- packages/components/effect-bridge/index.ts | 14 ++++-- .../components/effect-bridge/package.json | 2 +- packages/components/state/package.json | 2 +- packages/core/types/package.json | 2 +- packages/core/types/src/services/player.ts | 10 +++- .../broadcast-channel/package.json | 2 +- .../browser-crypto/package.json | 2 +- .../dexie-database/package.json | 2 +- .../mmb-metadata-provider/package.json | 2 +- .../onedrive-provider/package.json | 2 +- packages/services/bootstrap/package.json | 5 +- packages/services/bootstrap/src/layers.ts | 2 + packages/services/library/package.json | 2 +- packages/services/player/index.ts | 2 + packages/services/player/package.json | 15 ++++++ packages/services/player/src/player.ts | 49 +++++++++++++++++++ packages/services/player/src/state.ts | 9 ++++ packages/services/player/tsconfig.json | 7 +++ packages/web/package.json | 2 +- packages/workers/media-provider/package.json | 2 +- .../infrastructure/template/package.json.hbs | 22 +++------ .../services/template/package.json.hbs | 22 +++------ yarn.lock | 8 +-- 24 files changed, 135 insertions(+), 54 deletions(-) create mode 100644 packages/services/player/index.ts create mode 100644 packages/services/player/package.json create mode 100644 packages/services/player/src/player.ts create mode 100644 packages/services/player/src/state.ts create mode 100644 packages/services/player/tsconfig.json diff --git a/package.json b/package.json index 0c93483..88e404f 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@effect/schema": "^0.67.18", - "effect": "^3.2.8", + "effect": "^3.5.8", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/components/effect-bridge/index.ts b/packages/components/effect-bridge/index.ts index 0addfba..5020175 100644 --- a/packages/components/effect-bridge/index.ts +++ b/packages/components/effect-bridge/index.ts @@ -1,4 +1,4 @@ -import { Cause, Effect, Exit, Match, Stream } from "effect"; +import { Cause, Effect, Exit, Match, Sink, Stream } from "effect"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; type RunEffectCallback = () => void; @@ -179,8 +179,12 @@ export const useStream = ( Match.tag("initial", () => Effect.void), Match.tag("success", (stream) => stream.result.pipe( - Stream.runForEachChunk((albums) => - Effect.sync(() => setResult((prev) => [...prev, ...albums])), + Stream.run( + Sink.forEach((item) => + Effect.sync(() => { + setResult((current) => [...current, item]); + }), + ), ), ), ), @@ -189,7 +193,9 @@ export const useStream = ( ), Match.exhaustive, )(streamEffectState), - ); + ).then(() => { + console.log("Stream effect completed"); + }); }, [streamEffectState, matcher]); return [ diff --git a/packages/components/effect-bridge/package.json b/packages/components/effect-bridge/package.json index a688f51..6acc92c 100644 --- a/packages/components/effect-bridge/package.json +++ b/packages/components/effect-bridge/package.json @@ -7,7 +7,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "effect": "^3.2.8" + "effect": "^3.5.8" }, "devDependencies": { "@types/react": "^18.2.66", diff --git a/packages/components/state/package.json b/packages/components/state/package.json index 7b53de5..287533d 100644 --- a/packages/components/state/package.json +++ b/packages/components/state/package.json @@ -10,7 +10,7 @@ "@echo/components-effect-bridge": "^1.0.0", "@echo/core-types": "^1.0.0", "@echo/services-bootstrap": "^1.0.0", - "effect": "^3.2.8", + "effect": "^3.5.8", "jotai": "^2.8.3" }, "devDependencies": { diff --git a/packages/core/types/package.json b/packages/core/types/package.json index c165e9e..3c1b325 100644 --- a/packages/core/types/package.json +++ b/packages/core/types/package.json @@ -10,6 +10,6 @@ }, "dependencies": { "@effect/schema": "^0.67.18", - "effect": "^3.2.8" + "effect": "^3.5.8" } } \ No newline at end of file diff --git a/packages/core/types/src/services/player.ts b/packages/core/types/src/services/player.ts index 5a335bd..52a8002 100644 --- a/packages/core/types/src/services/player.ts +++ b/packages/core/types/src/services/player.ts @@ -1,5 +1,5 @@ -import { Context, Effect } from "effect"; -import type { Album } from "../model"; +import { Context, Effect, Stream } from "effect"; +import type { Album, PlayerState } from "../model"; /** * Service that provides a way to interact with the player and its state. @@ -10,6 +10,12 @@ export type Player = { * the playback to the appropriate media provider. */ readonly playAlbum: (album: Album) => Effect.Effect; + + /** + * Returns a stream that emits the current player state and any subsequent + * changes to it. + */ + readonly observe: Effect.Effect>; }; /** diff --git a/packages/infrastructure/broadcast-channel/package.json b/packages/infrastructure/broadcast-channel/package.json index 0b3c51d..cbf973e 100644 --- a/packages/infrastructure/broadcast-channel/package.json +++ b/packages/infrastructure/broadcast-channel/package.json @@ -10,6 +10,6 @@ }, "dependencies": { "@echo/core-types": "^1.0.0", - "effect": "^3.2.8" + "effect": "^3.5.8" } } \ No newline at end of file diff --git a/packages/infrastructure/browser-crypto/package.json b/packages/infrastructure/browser-crypto/package.json index 84050de..12d1078 100644 --- a/packages/infrastructure/browser-crypto/package.json +++ b/packages/infrastructure/browser-crypto/package.json @@ -10,6 +10,6 @@ }, "dependencies": { "@echo/core-types": "^1.0.0", - "effect": "^3.2.8" + "effect": "^3.5.8" } } \ No newline at end of file diff --git a/packages/infrastructure/dexie-database/package.json b/packages/infrastructure/dexie-database/package.json index 8483ddb..b67559d 100644 --- a/packages/infrastructure/dexie-database/package.json +++ b/packages/infrastructure/dexie-database/package.json @@ -12,6 +12,6 @@ "@echo/core-strings": "^1.0.0", "@echo/core-types": "^1.0.0", "dexie": "^4.0.7", - "effect": "^3.2.8" + "effect": "^3.5.8" } } \ No newline at end of file diff --git a/packages/infrastructure/mmb-metadata-provider/package.json b/packages/infrastructure/mmb-metadata-provider/package.json index b437009..90a6c4b 100644 --- a/packages/infrastructure/mmb-metadata-provider/package.json +++ b/packages/infrastructure/mmb-metadata-provider/package.json @@ -11,7 +11,7 @@ "dependencies": { "@echo/core-types": "^1.0.0", "buffer": "^6.0.3", - "effect": "^3.2.8", + "effect": "^3.5.8", "music-metadata": "^10.0.0", "process": "^0.11.10" } diff --git a/packages/infrastructure/onedrive-provider/package.json b/packages/infrastructure/onedrive-provider/package.json index 38feeeb..e56a0dd 100644 --- a/packages/infrastructure/onedrive-provider/package.json +++ b/packages/infrastructure/onedrive-provider/package.json @@ -13,7 +13,7 @@ "@echo/core-dates": "^1.0.0", "@echo/core-types": "^1.0.0", "@microsoft/microsoft-graph-client": "^3.0.7", - "effect": "^3.2.8" + "effect": "^3.5.8" }, "devDependencies": { "@microsoft/microsoft-graph-types": "^2.40.0" diff --git a/packages/services/bootstrap/package.json b/packages/services/bootstrap/package.json index e663495..1f7d674 100644 --- a/packages/services/bootstrap/package.json +++ b/packages/services/bootstrap/package.json @@ -13,10 +13,11 @@ "@echo/infrastructure-broadcast-channel": "^1.0.0", "@echo/infrastructure-browser-crypto": "^1.0.0", "@echo/infrastructure-dexie-database": "^1.0.0", - "@echo/services-library": "^1.0.0", "@echo/infrastructure-mmb-metadata-provider": "^1.0.0", "@echo/infrastructure-onedrive-provider": "^1.0.0", + "@echo/services-library": "^1.0.0", + "@echo/services-player": "^1.0.0", "@echo/workers-media-provider": "^1.0.0", - "effect": "^3.2.8" + "effect": "^3.5.8" } } \ No newline at end of file diff --git a/packages/services/bootstrap/src/layers.ts b/packages/services/bootstrap/src/layers.ts index 54e0f9e..9d1dbf0 100644 --- a/packages/services/bootstrap/src/layers.ts +++ b/packages/services/bootstrap/src/layers.ts @@ -9,6 +9,7 @@ import { LibraryLive } from "@echo/services-library"; import { MmbMetadataProviderLive } from "@echo/infrastructure-mmb-metadata-provider"; import { LazyLoadedProviderLive } from "./loaders/provider"; import { AppConfigLive } from "./app-config"; +import { PlayerLive } from "@echo/services-player"; /** * Exports a layer that can provide all dependencies that are needed in the @@ -17,6 +18,7 @@ import { AppConfigLive } from "./app-config"; export const MainLive = MediaProviderMainThreadBroadcastChannelLive.pipe( Layer.provideMerge(MediaProviderWorkerBroadcastChannelLive), Layer.provideMerge(BrowserCryptoLive), + Layer.provideMerge(PlayerLive), Layer.provideMerge(LazyLoadedProviderLive), Layer.provideMerge(LibraryLive), Layer.provideMerge(DexieDatabaseLive), diff --git a/packages/services/library/package.json b/packages/services/library/package.json index eecb816..da0c3c8 100644 --- a/packages/services/library/package.json +++ b/packages/services/library/package.json @@ -10,6 +10,6 @@ }, "dependencies": { "@echo/core-types": "^1.0.0", - "effect": "^3.2.8" + "effect": "^3.5.8" } } \ No newline at end of file diff --git a/packages/services/player/index.ts b/packages/services/player/index.ts new file mode 100644 index 0000000..6c75256 --- /dev/null +++ b/packages/services/player/index.ts @@ -0,0 +1,2 @@ +export { PlayerStateRef } from "./src/state"; +export { PlayerLive } from "./src/player"; diff --git a/packages/services/player/package.json b/packages/services/player/package.json new file mode 100644 index 0000000..bf10035 --- /dev/null +++ b/packages/services/player/package.json @@ -0,0 +1,15 @@ +{ + "name": "@echo/services-player", + "private": true, + "version": "1.0.0", + "description": "Contains the implementation for the Player service", + "main": "index.js", + "scripts": { + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@echo/core-types": "^1.0.0", + "effect": "^3.5.8" + } +} \ No newline at end of file diff --git a/packages/services/player/src/player.ts b/packages/services/player/src/player.ts new file mode 100644 index 0000000..0bd17fd --- /dev/null +++ b/packages/services/player/src/player.ts @@ -0,0 +1,49 @@ +import { Player, type PlayerState } from "@echo/core-types"; +import { Effect, Layer, Option, PubSub, Ref, Stream } from "effect"; +import { PlayerStateRef } from "./state"; + +const PlayerLiveWithState = Layer.effect( + Player, + Effect.gen(function* () { + const state = yield* PlayerStateRef; + const statePubSub = yield* PubSub.dropping({ + capacity: 1, + replay: 1, + }); + + // Yield initial state to subscribers. + yield* statePubSub.publish(yield* state.get); + + return Player.of({ + playAlbum: (_album) => + Effect.gen(function* () { + yield* Ref.update(state, (current) => ({ + ...current, + status: "playing" as const, + })); + + yield* statePubSub.publish(yield* state.get); + }), + observe: Effect.sync(() => + Stream.fromPubSub(statePubSub).pipe( + Stream.tap((state) => Effect.logInfo("Player state changed", state)), + Stream.ensuring(Effect.logInfo("Player state stream closed")), + ), + ), + }); + }), +); + +const PlayerStateLive = Layer.effect( + PlayerStateRef, + Ref.make({ + comingUpTracks: [], + previouslyPlayedTracks: [], + currentTrack: Option.none(), + status: "stopped", + } as PlayerState), +); + +export const PlayerLive = PlayerLiveWithState.pipe( + Layer.provide(PlayerStateLive), +); diff --git a/packages/services/player/src/state.ts b/packages/services/player/src/state.ts new file mode 100644 index 0000000..e37ded9 --- /dev/null +++ b/packages/services/player/src/state.ts @@ -0,0 +1,9 @@ +import type { PlayerState } from "@echo/core-types"; +import { Context, Ref } from "effect"; + +/** + * Tag that can provide a ref to the current state of the player. + */ +export class PlayerStateRef extends Context.Tag( + "@echo/services-player/PlayerStateRef", +)>() {} diff --git a/packages/services/player/tsconfig.json b/packages/services/player/tsconfig.json new file mode 100644 index 0000000..6953ff5 --- /dev/null +++ b/packages/services/player/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.json", + "include": [ + "src", + "index.ts" + ] +} \ No newline at end of file diff --git a/packages/web/package.json b/packages/web/package.json index a501975..09b1a01 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -15,7 +15,7 @@ "@echo/components-state": "^1.0.0", "@echo/core-types": "^1.0.0", "@echo/services-bootstrap": "^1.0.0", - "effect": "^3.2.8", + "effect": "^3.5.8", "jotai": "^2.8.3" }, "devDependencies": { diff --git a/packages/workers/media-provider/package.json b/packages/workers/media-provider/package.json index 26b5962..c43bd51 100644 --- a/packages/workers/media-provider/package.json +++ b/packages/workers/media-provider/package.json @@ -16,6 +16,6 @@ "@echo/infrastructure-browser-crypto": "^1.0.0", "@echo/services-bootstrap": "^1.0.0", "@effect/schema": "^0.67.18", - "effect": "^3.2.8" + "effect": "^3.5.8" } } \ No newline at end of file diff --git a/tools/plop-templates/infrastructure/template/package.json.hbs b/tools/plop-templates/infrastructure/template/package.json.hbs index 217c894..651b94d 100644 --- a/tools/plop-templates/infrastructure/template/package.json.hbs +++ b/tools/plop-templates/infrastructure/template/package.json.hbs @@ -1,15 +1,7 @@ -{ - "name": "@echo/infrastructure-{{dashCase name}}", - "private": true, - "version": "1.0.0", - "description": "Contains the {{properCase name}} related infrastructure", - "main": "index.js", - "scripts": { - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "typecheck": "tsc --noEmit" - }, - "dependencies": { - "@echo/core-types": "^1.0.0", - "effect": "^3.2.8" - } -} \ No newline at end of file +{ "name": "@echo/infrastructure-{{dashCase name}}", "private": true, "version": +"1.0.0", "description": "Contains the +{{properCase name}} +related infrastructure", "main": "index.js", "scripts": { "lint": "eslint . +--ext ts,tsx --report-unused-disable-directives --max-warnings 0", "typecheck": +"tsc --noEmit" }, "dependencies": { "@echo/core-types": "^1.0.0", "effect": +"^3.5.8" } } \ No newline at end of file diff --git a/tools/plop-templates/services/template/package.json.hbs b/tools/plop-templates/services/template/package.json.hbs index 4916e8d..6508bd2 100644 --- a/tools/plop-templates/services/template/package.json.hbs +++ b/tools/plop-templates/services/template/package.json.hbs @@ -1,15 +1,7 @@ -{ - "name": "@echo/services-{{dashCase name}}", - "private": true, - "version": "1.0.0", - "description": "Contains the implementation for the {{properCase name}} service", - "main": "index.js", - "scripts": { - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "typecheck": "tsc --noEmit" - }, - "dependencies": { - "@echo/core-types": "^1.0.0", - "effect": "^3.2.8" - } -} \ No newline at end of file +{ "name": "@echo/services-{{dashCase name}}", "private": true, "version": +"1.0.0", "description": "Contains the implementation for the +{{properCase name}} +service", "main": "index.js", "scripts": { "lint": "eslint . --ext ts,tsx +--report-unused-disable-directives --max-warnings 0", "typecheck": "tsc +--noEmit" }, "dependencies": { "@echo/core-types": "^1.0.0", "effect": "^3.5.8" +} } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index e64b443..24703f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1063,10 +1063,10 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -effect@^3.2.8: - version "3.2.8" - resolved "https://registry.yarnpkg.com/effect/-/effect-3.2.8.tgz#2517f72cf7041605243f3a1ec65859b3b6b39cd5" - integrity sha512-W9xHCXg+CKwvNb+/vEsfiBxSqyOEAyjo7mz1M7Kq5ZtxBUyefOsQxc3oGME7bTcxD4OozWcR27Sk4iYxHERoZg== +effect@^3.5.8: + version "3.5.8" + resolved "https://registry.yarnpkg.com/effect/-/effect-3.5.8.tgz#63098d38f538facd4d8b0ca8b81717395c33f694" + integrity sha512-gGrRH3BgsUrfOXx4vbD4puRS+/IyK7JiERhuKnjVYurTVDj3J+lPI7raJB9/AAKr59SGEiV7+Sy59/Q/Vfo5mQ== emoji-regex@^10.3.0: version "10.3.0"