Skip to content

Commit

Permalink
Partially re-implement user library in Rx
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepyfran committed Jul 29, 2024
1 parent 6dfe15e commit 79348ed
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 43 deletions.
1 change: 1 addition & 0 deletions packages/components/library/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { UserLibraryWithSuspense as UserLibrary } from "./src/Library";
21 changes: 21 additions & 0 deletions packages/components/library/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@echo/components-library",
"private": true,
"version": "1.0.0",
"scripts": {
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@echo/core-types": "^1.0.0",
"@echo/services-bootstrap": "^1.0.0",
"@echo/services-library": "^1.0.0",
"@effect-rx/rx": "^0.33.8",
"@effect-rx/rx-react": "^0.30.11",
"effect": "^3.2.8"
},
"devDependencies": {
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22"
}
}
55 changes: 55 additions & 0 deletions packages/components/library/src/Library.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Library, type Track } from "@echo/core-types";
import { MainLive } from "@echo/services-bootstrap";
import { Rx } from "@effect-rx/rx";
import { Layer, Match, Stream } from "effect";
import { Suspense, useState } from "react";
import { LibraryLive } from "@echo/services-library";
import { useRx } from "@effect-rx/rx-react";

const runtime = Rx.runtime(LibraryLive.pipe(Layer.provide(MainLive)));
const observeLibrary = runtime.pull(Stream.unwrap(Library.observeAlbums()), {
initialValue: [],
});

const UserLibrary = () => {
const [src, setSrc] = useState<string | undefined>(undefined);
const [result] = useRx(observeLibrary);

const playFirstTrack = (tracks: Track[]) => {
const track = tracks[0];
switch (track.resource.type) {
case "file":
setSrc(track.resource.uri);
break;
case "api":
break;
}
};

return Match.value(result).pipe(
Match.tag("Initial", () => <h5>Loading...</h5>),
// FIXME: Why does this render only one?
Match.tag("Success", ({ value: { items } }) => (
<div>
<br />
<audio src={src} autoPlay controls />
{items.map(([album, tracks]) => (
<div key={album.id}>
<h3>{album.name}</h3>
<p>{album.artist.name}</p>
<button onClick={() => playFirstTrack(tracks)}>Play</button>
<hr />
</div>
))}
</div>
)),
Match.tag("Failure", (error) => <h5>{JSON.stringify(error)}</h5>),
Match.exhaustive,
);
};

export const UserLibraryWithSuspense = () => (
<Suspense fallback="Loading library...">
<UserLibrary />
</Suspense>
);
1 change: 1 addition & 0 deletions packages/components/library/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
7 changes: 7 additions & 0 deletions packages/components/library/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"include": [
"src",
"index.ts"
],
"extends": "../../../tsconfig.json"
}
9 changes: 6 additions & 3 deletions packages/core/types/src/services/library.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Context, Effect, Stream } from "effect";
import { Effect, Stream } from "effect";
import type { Album, ArtistId, Track } from "../model";

/**
Expand All @@ -15,7 +15,7 @@ export class NonExistingArtistReferenced extends Error {
/**
* Service that provides access to the user's library.
*/
export type Library = {
export type ILibrary = {
/**
* Returns a stream of albums that are currently stored in the database.
*/
Expand All @@ -27,4 +27,7 @@ export type Library = {
/**
* Tag to identify the library service.
*/
export const Library = Context.GenericTag<Library>("@echo/core-types/Library");
export class Library extends Effect.Tag("@echo/core-types/Library")<
Library,
ILibrary
>() {}
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"dependencies": {
"@echo/components-add-provider": "^1.0.0",
"@echo/components-library": "^1.0.0",
"@echo/components-provider-status": "^1.0.0",
"@echo/core-types": "^1.0.0",
"@echo/services-bootstrap": "^1.0.0",
Expand Down
42 changes: 2 additions & 40 deletions packages/web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,11 @@
import { AddProvider } from "@echo/components-add-provider";
import { UserLibrary } from "@echo/components-library";
import { ProviderStatus } from "@echo/components-provider-status";

export const App = () => (
<div>
<AddProvider />
<ProviderStatus />
<UserLibrary />
</div>
);

// const observeLibrary = Effect.gen(function* () {
// const library = yield* Library;
// return yield* library.observeAlbums();
// }).pipe(Effect.provide(MainLive));

// const UserLibrary = () => {
// const [src, setSrc] = useState<string | undefined>(undefined);
// const [albumStream, matcher] = useStream(observeLibrary);

// const playFirstTrack = (tracks: Track[]) => {
// const track = tracks[0];
// switch (track.resource.type) {
// case "file":
// setSrc(track.resource.uri);
// break;
// case "api":
// break;
// }
// };

// return matcher.pipe(
// Match.tag("empty", () => <h1>Nothing in your library</h1>),
// Match.tag("items", ({ items }) => (
// <div>
// <audio src={src} autoPlay controls />
// {items.map(([album, tracks]) => (
// <div key={album.id}>
// <h1>{album.name}</h1>
// <p>{album.artist.name}</p>
// <button onClick={() => playFirstTrack(tracks)}>Play</button>
// <hr />
// </div>
// ))}
// </div>
// )),
// Match.tag("failure", () => <h1>Failed to load library</h1>),
// Match.exhaustive,
// )(albumStream);
// };

0 comments on commit 79348ed

Please sign in to comment.