elf integration

This commit is contained in:
raiper34
2024-06-23 18:16:54 +02:00
parent 46793174ab
commit 903697646d
10 changed files with 222 additions and 29 deletions
+10 -1
View File
@@ -6,7 +6,8 @@
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
"test": "ng test",
"gen": "ng generate"
},
"private": true,
"dependencies": {
@@ -19,6 +20,14 @@
"@angular/platform-browser-dynamic": "^18.0.0",
"@angular/router": "^18.0.0",
"@fortawesome/fontawesome-free": "^6.5.2",
"@ngneat/elf": "^2.5.1",
"@ngneat/elf-cli-ng": "^1.0.0",
"@ngneat/elf-devtools": "^1.3.0",
"@ngneat/elf-entities": "^5.0.2",
"@ngneat/elf-pagination": "^1.1.0",
"@ngneat/elf-persist-state": "^1.2.1",
"@ngneat/elf-requests": "^1.9.2",
"@ngneat/elf-state-history": "^1.4.0",
"bulma": "^1.0.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
+17 -8
View File
@@ -1,9 +1,10 @@
<section class="hero is-primary">
<div class="hero-body">
<p class="title">
<span><i class="fa-solid fa-music"></i> Spooty</span>
<i class="fa-brands fa-spotify"></i>
<span>Spooty</span>
</p>
<p class="subtitle"><i class="fa-solid fa-house"></i> Self-hosted <i class="fa-brands fa-spotify"></i> spotify <i class="fa-solid fa-download"></i>downloader</p>
<p class="subtitle">Self-hosted spotify downloader</p>
</div>
</section>
@@ -13,7 +14,7 @@
<p class="subtitle">Download</p>
<div class="is-flex">
<input class="input" type="text" [(ngModel)]="url" placeholder="Paste playlist/song/artist url"/>
<button class="button is-primary" (click)="download()">
<button class="button is-primary" [class.is-loading]="(createLoading$ | async)?.isLoading" (click)="download()">
<i class="fa-solid fa-download"></i> Download
</button>
</div>
@@ -21,18 +22,25 @@
<hr>
<div class="box">
<div class="is-flex is-align-items-center">
<p class="subtitle">List</p>
<button class="button" (click)="get()"><i class="fa-solid fa-arrows-rotate"></i> Refresh</button>
<p class="subtitle">List</p>&nbsp;
<i class="fa-solid fa-arrows-rotate is-clickable" [class.loading-animation]="(loading$ | async)?.isLoading" (click)="fetchPlaylists()"></i>
</div>
<article class="panel is-primary" *ngFor="let playlist of playlists">
<article class="panel is-primary" *ngFor="let playlist of playlists$ | async">
<p class="panel-heading is-flex is-justify-content-space-between">
<span><i class="fa-solid fa-caret-down"></i> {{playlist.name}}</span>
<span>
<i class="is-clickable fa-solid"
[ngClass]="playlist.collapsed ? 'fa-caret-up' : 'fa-caret-down'"
(click)="toggleCollapse(playlist.id)"
></i>&nbsp;
<span>{{playlist.name}}</span>
</span>
<a [href]="playlist.spotifyUrl" target="_blank" class="is-color-black"><i class="fa-brands fa-spotify"></i></a>
</p>
<ng-container *ngIf="playlist.collapsed">
<a class="panel-block is-flex is-justify-content-space-between" *ngFor="let track of playlist.tracks">
<div>
<a href="http://localhost:3000/track/download/{{track.id}}" class="panel-icon is-color-info" download>
<a href="api/track/download/{{track.id}}" class="panel-icon is-color-info" download>
<i class="fa-solid fa-download"></i>
</a>
<span>{{track.artist}} - {{track.song}}</span>&nbsp;
@@ -51,6 +59,7 @@
</ng-container>
</div>
</a>
</ng-container>
</article>
</div>
</div>
+15
View File
@@ -1,7 +1,22 @@
.panel-heading {
padding: 5px 10px;
border-radius: 10px;
}
.panel-block {
padding: 5px 10px;
}
.panel {
margin: 5px 0;
}
.loading-animation {
animation: spin 0.3s linear infinite;
}
@keyframes spin {
100% {
transform:rotate(360deg);
}
}
+15 -13
View File
@@ -1,34 +1,36 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {HttpClient} from "@angular/common/http";
import {FormsModule} from "@angular/forms";
import {NgFor, NgSwitch, NgSwitchCase} from "@angular/common";
import {CommonModule, NgFor, NgSwitch, NgSwitchCase} from "@angular/common";
import {PlaylistService} from "./services/playlist.service";
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, FormsModule, NgFor, NgSwitch, NgSwitchCase],
imports: [CommonModule, RouterOutlet, FormsModule, NgFor, NgSwitch, NgSwitchCase],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
url = ''
playlists: any[] = [];
tracks = [
{code: 'CC', name: 'Aaaa'},
{code: 'CrC', name: 'Aaaadfdfd'},
]
playlists$ = this.playlistService.all$;
loading$ = this.playlistService.loading$;
createLoading$ = this.playlistService.createLoading$;
constructor(private readonly http: HttpClient) {
this.get();
constructor(private readonly playlistService: PlaylistService) {
this.fetchPlaylists();
}
fetchPlaylists(): void {
this.playlistService.fetch();
}
download(): void {
this.http.post('/api/playlist', {spotifyUrl: this.url}).subscribe(() => this.get());
this.playlistService.create(this.url);
}
get(): void {
this.http.get('/api/playlist').subscribe(data => this.playlists = data as any);
toggleCollapse(playlistId: number): void {
this.playlistService.toggleCollapsed(playlistId);
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { PlaylistService } from './playlist.service';
describe('PlaylistService', () => {
let service: PlaylistService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(PlaylistService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,72 @@
import { Injectable } from '@angular/core';
import {createStore} from "@ngneat/elf";
import {HttpClient} from "@angular/common/http";
import {
addEntities,
selectAllEntities, selectEntities,
setEntities,
UIEntitiesRef,
unionEntities, updateEntities,
withEntities,
withUIEntities
} from "@ngneat/elf-entities";
import {joinRequestResult, trackRequestResult} from "@ngneat/elf-requests";
import {tap} from "rxjs";
const STORE_NAME = 'playlist';
const ENDPOINT = '/api/playlist';
const CREATE_LOADING = 'CREATE_LOADING';
export interface Playlist {
id: number;
name: string;
spotifyUrl: string;
tracks?: any[]; //todo fix it
}
export interface PlaylistUi {
id: number,
collapsed: boolean;
}
@Injectable({
providedIn: 'root'
})
export class PlaylistService {
private store = createStore(
{ name: STORE_NAME },
withEntities<Playlist>(),
withUIEntities<PlaylistUi>()
);
all$ = this.store.combine({
entities: this.store.pipe(selectAllEntities()),
UIEntities: this.store.pipe(selectEntities({ ref: UIEntitiesRef })),
}).pipe(unionEntities());
loading$ = this.store.pipe(joinRequestResult([STORE_NAME]));
createLoading$ = this.store.pipe(joinRequestResult([CREATE_LOADING], { initialStatus: 'idle' }));
constructor(private readonly http: HttpClient) {
}
fetch(): void {
this.http.get<Playlist[]>(ENDPOINT).pipe(
tap((data: Playlist[]) => this.store.update(
setEntities(data),
setEntities(data.map(item => ({id: item.id, collapsed: false})), {ref: UIEntitiesRef})
)),
trackRequestResult([STORE_NAME], { skipCache: true }),
).subscribe();
}
create(spotifyUrl: string): void {
this.http.post(ENDPOINT, {spotifyUrl}).pipe(
trackRequestResult([CREATE_LOADING], { skipCache: true })
).subscribe();
}
toggleCollapsed(id: number): void {
this.store.update(updateEntities(id, old => ({...old, collapsed: !old.collapsed}), { ref: UIEntitiesRef }))
}
}
+2
View File
@@ -1,6 +1,8 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
import { devTools } from '@ngneat/elf-devtools';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
devTools();