refactor(frontend): refactor frontend services code
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import {Component, Input} from '@angular/core';
|
import {Component, Input} from '@angular/core';
|
||||||
import {AsyncPipe, CommonModule, NgForOf, NgIf} from "@angular/common";
|
import {AsyncPipe, CommonModule, NgForOf, NgIf} from "@angular/common";
|
||||||
import {TrackListComponent} from "../track-list/track-list.component";
|
import {TrackListComponent} from "../track-list/track-list.component";
|
||||||
import {Playlist, PlaylistService, PlaylistStatusEnum, PlaylistUi} from "../../services/playlist.service";
|
import {PlaylistService, PlaylistStatusEnum, PlaylistUi} from "../../services/playlist.service";
|
||||||
import {Observable, map} from "rxjs";
|
import {Observable, map} from "rxjs";
|
||||||
|
import {Playlist} from "../../models/playlist";
|
||||||
|
|
||||||
const STATUS2CLASS = {
|
const STATUS2CLASS = {
|
||||||
[PlaylistStatusEnum.Completed]: 'is-success',
|
[PlaylistStatusEnum.Completed]: 'is-success',
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import {Component, Input} from '@angular/core';
|
import {Component, Input} from '@angular/core';
|
||||||
import {CommonModule, NgFor, NgSwitch, NgSwitchCase} from "@angular/common";
|
import {CommonModule, NgFor, NgSwitch, NgSwitchCase} from "@angular/common";
|
||||||
import {Track, TrackService, TrackStatusEnum} from "../../services/track.service";
|
import {TrackService} from "../../services/track.service";
|
||||||
import {Observable} from "rxjs";
|
import {Observable} from "rxjs";
|
||||||
|
import {Track, TrackStatusEnum} from "../../models/track";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-track-list',
|
selector: 'app-track-list',
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export interface Playlist {
|
||||||
|
id: number;
|
||||||
|
name?: string;
|
||||||
|
spotifyUrl: string;
|
||||||
|
error?: string;
|
||||||
|
active: boolean;
|
||||||
|
createdAt: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
export interface Track {
|
||||||
|
id: number;
|
||||||
|
artist: string;
|
||||||
|
name: string;
|
||||||
|
spotifyUrl: string;
|
||||||
|
youtubeUrl: string;
|
||||||
|
status: TrackStatusEnum;
|
||||||
|
playlistId?: number;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TrackStatusEnum {
|
||||||
|
New,
|
||||||
|
Searching,
|
||||||
|
Queued,
|
||||||
|
Downloading,
|
||||||
|
Completed,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
@@ -3,9 +3,7 @@ import {createStore} from "@ngneat/elf";
|
|||||||
import {HttpClient} from "@angular/common/http";
|
import {HttpClient} from "@angular/common/http";
|
||||||
import {
|
import {
|
||||||
deleteEntities,
|
deleteEntities,
|
||||||
getEntityByPredicate,
|
|
||||||
selectAllEntities, selectEntities, selectEntity,
|
selectAllEntities, selectEntities, selectEntity,
|
||||||
selectEntityByPredicate,
|
|
||||||
setEntities,
|
setEntities,
|
||||||
UIEntitiesRef,
|
UIEntitiesRef,
|
||||||
unionEntities, updateEntities, upsertEntities,
|
unionEntities, updateEntities, upsertEntities,
|
||||||
@@ -14,20 +12,17 @@ import {
|
|||||||
} from "@ngneat/elf-entities";
|
} from "@ngneat/elf-entities";
|
||||||
import {joinRequestResult, trackRequestResult} from "@ngneat/elf-requests";
|
import {joinRequestResult, trackRequestResult} from "@ngneat/elf-requests";
|
||||||
import {combineLatest, filter, first, map, Observable, of, switchMap, tap} from "rxjs";
|
import {combineLatest, filter, first, map, Observable, of, switchMap, tap} from "rxjs";
|
||||||
import {Track, TrackService} from "./track.service";
|
import {TrackService} from "./track.service";
|
||||||
import {Socket} from "ngx-socket-io";
|
import {Socket} from "ngx-socket-io";
|
||||||
|
import {Playlist} from "../models/playlist";
|
||||||
|
|
||||||
const STORE_NAME = 'playlist';
|
const STORE_NAME = 'playlist';
|
||||||
const ENDPOINT = '/api/playlist';
|
const ENDPOINT = '/api/playlist';
|
||||||
const CREATE_LOADING = 'CREATE_LOADING';
|
const CREATE_LOADING = 'CREATE_LOADING';
|
||||||
|
enum WsPlaylistOperation {
|
||||||
export interface Playlist {
|
New = 'playlistNew',
|
||||||
id: number;
|
Update = 'playlistUpdate',
|
||||||
name?: string;
|
Delete = 'playlistDelete',
|
||||||
spotifyUrl: string;
|
|
||||||
error?: string;
|
|
||||||
active: boolean;
|
|
||||||
createdAt: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlaylistUi {
|
export interface PlaylistUi {
|
||||||
@@ -53,26 +48,18 @@ export class PlaylistService {
|
|||||||
withEntities<Playlist>(),
|
withEntities<Playlist>(),
|
||||||
withUIEntities<PlaylistUi>()
|
withUIEntities<PlaylistUi>()
|
||||||
);
|
);
|
||||||
|
|
||||||
all$ = this.store.combine({
|
all$ = this.store.combine({
|
||||||
entities: this.store.pipe(selectAllEntities()),
|
entities: this.store.pipe(selectAllEntities()),
|
||||||
UIEntities: this.store.pipe(selectEntities({ ref: UIEntitiesRef })),
|
UIEntities: this.store.pipe(selectEntities({ ref: UIEntitiesRef })),
|
||||||
}).pipe(unionEntities(), map(data => data.sort((a, b) => this.sort(a, b))));
|
}).pipe(unionEntities(), map(data => data.sort((a, b) => this.groupActiveAndSortByCreation(a, b))));
|
||||||
|
|
||||||
loading$ = this.store.pipe(joinRequestResult([STORE_NAME]));
|
|
||||||
createLoading$ = this.store.pipe(joinRequestResult([CREATE_LOADING], { initialStatus: 'idle' }));
|
createLoading$ = this.store.pipe(joinRequestResult([CREATE_LOADING], { initialStatus: 'idle' }));
|
||||||
|
|
||||||
constructor(private readonly http: HttpClient,
|
constructor(private readonly http: HttpClient,
|
||||||
private readonly socket: Socket,
|
private readonly socket: Socket,
|
||||||
private readonly trackService: TrackService,
|
private readonly trackService: TrackService,
|
||||||
) {
|
) {
|
||||||
this.socket.on('playlistUpdate', (playlist: Playlist) => this.store.update(upsertEntities(playlist)));
|
this.initWsConnection();
|
||||||
this.socket.on('playlistDelete', ({id}: {id: number}) => this.store.update(deleteEntities(Number(id))));
|
|
||||||
this.socket.on('playlistNew', (playlist: Playlist) =>
|
|
||||||
this.store.update(
|
|
||||||
upsertEntities(playlist),
|
|
||||||
upsertEntities({id: playlist.id, collapsed: false}, {ref: UIEntitiesRef})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getById(id: number): Observable<Playlist | undefined> {
|
getById(id: number): Observable<Playlist | undefined> {
|
||||||
@@ -133,7 +120,6 @@ export class PlaylistService {
|
|||||||
setEntities(data.map(item => ({id: item.id, collapsed: false})), {ref: UIEntitiesRef})
|
setEntities(data.map(item => ({id: item.id, collapsed: false})), {ref: UIEntitiesRef})
|
||||||
)),
|
)),
|
||||||
tap((data: Playlist[]) => data.forEach(playlist => this.trackService.fetch(playlist.id))),
|
tap((data: Playlist[]) => data.forEach(playlist => this.trackService.fetch(playlist.id))),
|
||||||
trackRequestResult([STORE_NAME], { skipCache: true }),
|
|
||||||
).subscribe();
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,17 +130,13 @@ export class PlaylistService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleCollapsed(id: number): void {
|
toggleCollapsed(id: number): void {
|
||||||
this.store.update(updateEntities(id, old => ({...old, collapsed: !old.collapsed}), { ref: UIEntitiesRef }))
|
this.store.update(updateEntities(id, old => ({...old, collapsed: !old.collapsed}), { ref: UIEntitiesRef }));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(id: number): void {
|
delete(id: number): void {
|
||||||
this.delete$(id).subscribe();
|
this.delete$(id).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
private delete$(id: number): Observable<void> {
|
|
||||||
return this.http.delete<void>(`${ENDPOINT}/${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
retryFailed(id: number): void {
|
retryFailed(id: number): void {
|
||||||
this.http.get<void>(`${ENDPOINT}/retry/${id}`).subscribe();
|
this.http.get<void>(`${ENDPOINT}/retry/${id}`).subscribe();
|
||||||
}
|
}
|
||||||
@@ -163,7 +145,22 @@ export class PlaylistService {
|
|||||||
this.http.put<void>(`${ENDPOINT}/${id}`, {active}).subscribe();
|
this.http.put<void>(`${ENDPOINT}/${id}`, {active}).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
private sort(a: Playlist & PlaylistUi, b: Playlist & PlaylistUi): number {
|
private delete$(id: number): Observable<void> {
|
||||||
|
return this.http.delete<void>(`${ENDPOINT}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private groupActiveAndSortByCreation(a: Playlist & PlaylistUi, b: Playlist & PlaylistUi): number {
|
||||||
return a.active === b.active ? (b.createdAt - a.createdAt) : (a.active < b.active ? 1 : -1);
|
return a.active === b.active ? (b.createdAt - a.createdAt) : (a.active < b.active ? 1 : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private initWsConnection(): void {
|
||||||
|
this.socket.on(WsPlaylistOperation.Update, (playlist: Playlist) => this.store.update(upsertEntities(playlist)));
|
||||||
|
this.socket.on(WsPlaylistOperation.Delete, ({id}: {id: number}) => this.store.update(deleteEntities(Number(id))));
|
||||||
|
this.socket.on(WsPlaylistOperation.New, (playlist: Playlist) =>
|
||||||
|
this.store.update(
|
||||||
|
upsertEntities(playlist),
|
||||||
|
upsertEntities({id: playlist.id, collapsed: false}, {ref: UIEntitiesRef})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,30 +3,15 @@ import {createStore} from "@ngneat/elf";
|
|||||||
import {deleteEntities, selectManyByPredicate, upsertEntities, withEntities} from "@ngneat/elf-entities";
|
import {deleteEntities, selectManyByPredicate, upsertEntities, withEntities} from "@ngneat/elf-entities";
|
||||||
import {Socket} from "ngx-socket-io";
|
import {Socket} from "ngx-socket-io";
|
||||||
import {map, Observable, tap} from "rxjs";
|
import {map, Observable, tap} from "rxjs";
|
||||||
import {trackRequestResult} from "@ngneat/elf-requests";
|
|
||||||
import {HttpClient} from "@angular/common/http";
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
import {Track, TrackStatusEnum} from "../models/track";
|
||||||
|
|
||||||
const STORE_NAME = 'track';
|
const STORE_NAME = 'track';
|
||||||
const ENDPOINT = '/api/track';
|
const ENDPOINT = '/api/track';
|
||||||
|
enum WsTrackOperation {
|
||||||
export interface Track {
|
New = 'trackNew',
|
||||||
id: number;
|
Update = 'trackUpdate',
|
||||||
artist: string;
|
Delete = 'trackDelete',
|
||||||
name: string;
|
|
||||||
spotifyUrl: string;
|
|
||||||
youtubeUrl: string;
|
|
||||||
status: TrackStatusEnum;
|
|
||||||
playlistId?: number;
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum TrackStatusEnum {
|
|
||||||
New,
|
|
||||||
Searching,
|
|
||||||
Queued,
|
|
||||||
Downloading,
|
|
||||||
Completed,
|
|
||||||
Error,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@@ -58,17 +43,12 @@ export class TrackService {
|
|||||||
private readonly http: HttpClient,
|
private readonly http: HttpClient,
|
||||||
private readonly socket: Socket,
|
private readonly socket: Socket,
|
||||||
) {
|
) {
|
||||||
this.socket.on('trackUpdate', (track: Track) => this.store.update(upsertEntities(track)));
|
this.initWsConnection();
|
||||||
this.socket.on('trackDelete', ({id}: {id: number}) => this.store.update(deleteEntities(id)));
|
|
||||||
this.socket.on('trackNew', ({track, playlistId}: {track: Track, playlistId: number}) =>
|
|
||||||
this.store.update(upsertEntities([{...track, playlistId}]))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(playlistId: number): void {
|
fetch(playlistId: number): void {
|
||||||
this.http.get<Track[]>(`${ENDPOINT}/playlist/${playlistId}`).pipe(
|
this.http.get<Track[]>(`${ENDPOINT}/playlist/${playlistId}`).pipe(
|
||||||
tap((data: Track[]) => this.store.update(upsertEntities(data.map(track => ({...track, playlistId}))))),
|
tap((data: Track[]) => this.store.update(upsertEntities(data.map(track => ({...track, playlistId}))))),
|
||||||
trackRequestResult([STORE_NAME], { skipCache: true }),
|
|
||||||
).subscribe();
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,4 +59,12 @@ export class TrackService {
|
|||||||
retry(id: number): void {
|
retry(id: number): void {
|
||||||
this.http.get(`${ENDPOINT}/retry/${id}`).subscribe();
|
this.http.get(`${ENDPOINT}/retry/${id}`).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private initWsConnection(): void {
|
||||||
|
this.socket.on(WsTrackOperation.Update, (track: Track) => this.store.update(upsertEntities(track)));
|
||||||
|
this.socket.on(WsTrackOperation.Delete, ({id}: {id: number}) => this.store.update(deleteEntities(id)));
|
||||||
|
this.socket.on(WsTrackOperation.New, ({track, playlistId}: {track: Track, playlistId: number}) =>
|
||||||
|
this.store.update(upsertEntities([{...track, playlistId}]))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user