diff --git a/packages/components/add-provider/src/add-provider.ts b/packages/components/add-provider/src/add-provider.ts index f0d11fe..ae06548 100644 --- a/packages/components/add-provider/src/add-provider.ts +++ b/packages/components/add-provider/src/add-provider.ts @@ -61,10 +61,10 @@ export class AddProvider extends LitElement { Match.tag( "ProviderStarted", () => - html`

- Provider started! Check the status on the top bar for more - information -

`, + html`
+ Provider started! Your tracks will now be scanned and added to your + library. Check the status on the top bar for the latest status. +
`, ), Match.exhaustive, ); diff --git a/packages/components/add-provider/src/provider-loader.ts b/packages/components/add-provider/src/provider-loader.ts index 9e0e2a7..802dedd 100644 --- a/packages/components/add-provider/src/provider-loader.ts +++ b/packages/components/add-provider/src/provider-loader.ts @@ -5,7 +5,7 @@ import { type ProviderMetadata, } from "@echo/core-types"; import { Match } from "effect"; -import { LitElement, html } from "lit"; +import { LitElement, css, html } from "lit"; import { customElement, property } from "lit/decorators.js"; /** @@ -60,30 +60,66 @@ export class ProviderLoader extends LitElement { }, ); + static styles = css` + .available-provider-list { + display: flex; + justify-content: center; + gap: 1rem; + } + + button { + margin-top: 10px; + padding: 5px 10px; + background-color: #fff; + color: #000; + border: 1px solid #000; + cursor: pointer; + font-size: 1em; + text-transform: uppercase; + } + + button:hover { + background-color: #000; + color: #fff; + } + `; + render() { - return Match.value(this._loaderStatus).pipe( - Match.tag("Initial", () => - this.availableProviders.map( - (provider) => html` - + `, + )} + + `, + ), + Match.tag( + "WaitingToConnect", + ({ metadata }) => html` + `, ), - ), - Match.tag( - "WaitingToConnect", - ({ metadata }) => html` - - `, - ), - Match.tag("LoadingProvider", () => html`

Loading provider...

`), - Match.tag("ConnectingToProvider", () => html`

Connecting...

`), - Match.tag("Connected", () => html`

Connected!

`), - Match.exhaustive, - ); + Match.tag("LoadingProvider", () => html`
Loading provider...
`), + Match.tag("ConnectingToProvider", () => html`
Connecting...
`), + Match.tag("Connected", () => html`
Connected!
`), + Match.exhaustive, + )} + `; } } diff --git a/packages/components/add-provider/src/select-root.ts b/packages/components/add-provider/src/select-root.ts index ba23ff8..82df7bc 100644 --- a/packages/components/add-provider/src/select-root.ts +++ b/packages/components/add-provider/src/select-root.ts @@ -1,6 +1,6 @@ import { EffectFn } from "@echo/components-shared-controllers/src/effect-fn.controller"; import { AddProviderWorkflow, type FolderMetadata } from "@echo/core-types"; -import { LitElement, html, nothing } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators.js"; /** @@ -30,18 +30,44 @@ export class SelectRoot extends LitElement { }, ); + static styles = css` + .available-folder-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 10; + } + + button { + margin-top: 10px; + padding: 5px 10px; + background-color: #fff; + color: #000; + border: 1px solid #000; + cursor: pointer; + font-size: 1em; + text-transform: uppercase; + } + + button:hover { + background-color: #000; + color: #fff; + } + `; + render() { return this._selectRoot.render({ initial: () => html`

Select a root folder:

- ${this.availableFolders.map( - (folder) => - html``, - )} +
+ ${this.availableFolders.map( + (folder) => + html``, + )} +
`, - pending: () => html`

Connecting...

`, + pending: () => html`
Connecting...
`, complete: () => nothing, }); } diff --git a/packages/components/provider-status/package.json b/packages/components/provider-status/package.json index 2d9fe86..7c73e0a 100644 --- a/packages/components/provider-status/package.json +++ b/packages/components/provider-status/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@echo/core-types": "^1.0.0", + "@echo/components-add-provider": "^1.0.0", "@echo/components-icons": "^1.0.0", "@echo/components-shared-controllers": "^1.0.0", "@echo/services-bootstrap-runtime": "^1.0.0", diff --git a/packages/components/provider-status/src/provider-status.ts b/packages/components/provider-status/src/provider-status.ts index ce47ef1..7d7b642 100644 --- a/packages/components/provider-status/src/provider-status.ts +++ b/packages/components/provider-status/src/provider-status.ts @@ -5,10 +5,11 @@ import { } from "@echo/core-types"; import { StreamConsumer } from "@echo/components-shared-controllers"; import { LitElement, css, html, nothing } from "lit"; -import { customElement } from "lit/decorators.js"; +import { customElement, query } from "lit/decorators.js"; import { map } from "lit/directives/map.js"; import { Match } from "effect"; import "@echo/components-icons"; +import "@echo/components-add-provider"; /** * Component that displays the status of all active providers. @@ -20,6 +21,9 @@ export class AllProvidersStatusBar extends LitElement { MediaProviderStatus.observe, ); + @query("#add-provider") + private _addProviderDialog!: HTMLDialogElement; + static styles = css` .provider-container { display: flex; @@ -41,6 +45,25 @@ export class AllProvidersStatusBar extends LitElement { animation: blinking 1s infinite; } + dialog[open] { + display: flex; + flex-direction: column; + height: 50%; + width: 50%; + } + + dialog[open]::backdrop { + background-color: rgb(0 0 0 / 75%); + } + + dialog .dismiss { + align-self: flex-end; + padding: 0.5rem; + font-size: 1.5rem; + background: none; + border: none; + } + @keyframes blinking { 0% { opacity: 1; @@ -57,11 +80,11 @@ export class AllProvidersStatusBar extends LitElement { render() { return this._providerStatus.render({ initial: () => nothing, - item: (status) => - map( - status, - ([providerId, providerStatus]) => html` -
+ item: (status) => html` +
+ ${map( + status, + ([providerId, providerStatus]) => html`
-
- `, - ), + `, + )} + +
+ ${this._renderAddProviderModal()} + `, complete: () => nothing, error: () => nothing, }); @@ -116,6 +142,22 @@ export class AllProvidersStatusBar extends LitElement { Match.orElse(() => `Syncing ${providerId}`), ); } + + private _renderAddProviderModal() { + return html` + + + + + `; + } + + // --- Event handlers --- + private _onAddProviderClick() { + this._addProviderDialog.showModal(); + } } declare global { diff --git a/packages/web/package.json b/packages/web/package.json index 0710bc2..371b9a7 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -11,7 +11,6 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@echo/components-add-provider": "^1.0.0", "@echo/components-library": "^1.0.0", "@echo/components-player": "^1.0.0", "@echo/components-provider-status": "^1.0.0", diff --git a/packages/web/src/main.ts b/packages/web/src/main.ts index f31b1a4..8ca4a9d 100644 --- a/packages/web/src/main.ts +++ b/packages/web/src/main.ts @@ -1,11 +1,14 @@ import { LitElement, css, html } from "lit"; -import { customElement } from "lit/decorators.js"; -import { AppInit } from "@echo/core-types"; -import { EffectConsumer } from "@echo/components-shared-controllers"; -import "@echo/components-add-provider"; +import { customElement, query } from "lit/decorators.js"; +import { AppInit, MediaProviderStatus } from "@echo/core-types"; +import { + EffectConsumer, + StreamConsumer, +} from "@echo/components-shared-controllers"; import "@echo/components-library"; import "@echo/components-player"; import "@echo/components-provider-status"; +import { cache } from "lit/directives/cache.js"; /** * Root element of the application. @@ -13,34 +16,118 @@ import "@echo/components-provider-status"; @customElement("app-root") export class MyElement extends LitElement { private _init = new EffectConsumer(this, AppInit.init); + private _providerStatus = new StreamConsumer( + this, + MediaProviderStatus.observe, + ); + + @query("#add-provider") + private _addProviderDialog!: HTMLDialogElement; static styles = css` header { display: flex; - justify-content: space-between; + justify-content: flex-end; align-items: center; padding: 0 1rem; } + + button { + margin-top: 10px; + padding: 5px 10px; + background-color: #fff; + color: #000; + border: 1px solid #000; + cursor: pointer; + font-size: 1em; + text-transform: uppercase; + } + + button:hover { + background-color: #000; + color: #fff; + } + + dialog[open] { + display: flex; + flex-direction: column; + height: 50%; + width: 50%; + } + + dialog[open]::backdrop { + background-color: rgb(0 0 0 / 75%); + } + + dialog .dismiss { + align-self: flex-end; + padding: 0.5rem; + font-size: 1.5rem; + background: none; + border: none; + } `; render() { - return this._init.render({ - initial: () => html`

Initializing Echo...

`, - complete: () => html` -
-
- - -
- - -
- `, - error: () => - html`

- Ooops, something went wrong. Please report it! -

`, - }); + return html` + ${this._init.render({ + initial: () => html`

Initializing Echo...

`, + complete: () => + cache( + this._providerStatus.render({ + item: (status) => + status.size > 0 + ? this._renderMainPage() + : html` +
+

Welcome to Echo!

+

+ Echo is a library manager and music player that can + stream from a variety of different sources. +

+

To get started, add a provider:

+ +
+ `, + }), + ), + error: () => + html`

+ Ooops, something went wrong. Please report it! +

`, + })} + ${this._renderAddProviderModal()} + `; + } + + private _renderMainPage() { + return html` +
+
+ +
+ + +
+ `; + } + + private _renderAddProviderModal() { + return html` + + + + + `; + } + + // --- Event handlers --- + private _onAddProviderClick() { + this._addProviderDialog.showModal(); } }