From 932eb97bf523882c4fcbe547b5a9b27c0b3540be Mon Sep 17 00:00:00 2001 From: 4gray Date: Fri, 17 Mar 2023 21:54:09 +0100 Subject: [PATCH] feat: export/import/delete all playlists --- src/app/services/playlists.service.ts | 22 +++++++------ src/app/settings/settings.component.html | 2 +- src/app/settings/settings.component.ts | 41 +++++++++++++++++++----- src/app/state/actions.ts | 4 +++ src/app/state/effects.ts | 25 ++++++++++++++- src/app/state/reducers.ts | 6 ++++ src/assets/i18n/en.json | 15 +++++---- 7 files changed, 90 insertions(+), 25 deletions(-) diff --git a/src/app/services/playlists.service.ts b/src/app/services/playlists.service.ts index 443ecadd5..e4d30fc48 100644 --- a/src/app/services/playlists.service.ts +++ b/src/app/services/playlists.service.ts @@ -7,12 +7,12 @@ import { Channel } from '../../../shared/channel.interface'; import { GLOBAL_FAVORITES_PLAYLIST_ID } from '../../../shared/constants'; import { Playlist, - PlaylistUpdateState + PlaylistUpdateState, } from '../../../shared/playlist.interface'; import { aggregateFavoriteChannels, createFavoritesPlaylist, - createPlaylistObject + createPlaylistObject, } from '../../../shared/playlist.utils'; import { DbStores } from '../indexed-db.config'; import { PlaylistMeta } from '../shared/playlist-meta.type'; @@ -193,10 +193,16 @@ export class PlaylistsService { } getRawPlaylistById(id: string) { - return this.dbService.getByID(DbStores.Playlists, id).pipe(map(playlist => { - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - return playlist.playlist.header.raw + '\n' + playlist.playlist.items.map(item => item.raw).join('\n'); - })); + return this.dbService.getByID(DbStores.Playlists, id).pipe( + map((playlist) => { + return ( + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + playlist.playlist.header.raw + + '\n' + + playlist.playlist.items.map((item) => item.raw).join('\n') + ); + }) + ); } getAllData() { @@ -204,8 +210,6 @@ export class PlaylistsService { } removeAll() { - this.dbService.clear(DbStores.Playlists).subscribe(data => { - console.log(data); - }); + return this.dbService.clear(DbStores.Playlists); } } diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index f4a812d33..2d5144856 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -216,7 +216,7 @@ " color="warn" > - {{ 'SETTINGS.REMOVE_ALL' | translate }} + {{ 'SETTINGS.REMOVE_ALL_BUTTON' | translate }} diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 424a04f90..766b5902f 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -13,6 +13,7 @@ import { TranslateService } from '@ngx-translate/core'; import { take } from 'rxjs'; import * as semver from 'semver'; import { EPG_FORCE_FETCH } from '../../../shared/ipc-commands'; +import { Playlist } from '../../../shared/playlist.interface'; import { DataService } from '../services/data.service'; import { DialogService } from '../services/dialog.service'; import { EpgService } from '../services/epg.service'; @@ -302,15 +303,38 @@ export class SettingsComponent implements OnInit { const reader = new FileReader(); reader.onload = () => { const contents = reader.result; - console.log(contents); - const parsedPlaylists = JSON.parse(contents.toString()); + try { + const parsedPlaylists: Playlist[] = JSON.parse( + contents.toString() + ); - this.store.dispatch( - PlaylistActions.addManyPlaylists({ - playlists: parsedPlaylists, - }) - ); + if (!Array.isArray(parsedPlaylists)) { + this.snackBar.open( + this.translate.instant( + 'SETTINGS.IMPORT_ERROR' + ), + null, + { + duration: 2000, + } + ); + } else { + this.store.dispatch( + PlaylistActions.addManyPlaylists({ + playlists: parsedPlaylists, + }) + ); + } + } catch (error) { + this.snackBar.open( + this.translate.instant('SETTINGS.IMPORT_ERROR'), + null, + { + duration: 2000, + } + ); + } }; reader.readAsText(file); } @@ -324,7 +348,8 @@ export class SettingsComponent implements OnInit { this.dialogService.openConfirmDialog({ title: this.translate.instant('SETTINGS.REMOVE_DIALOG.TITLE'), message: this.translate.instant('SETTINGS.REMOVE_DIALOG.MESSAGE'), - onConfirm: (): void => this.playlistsService.removeAll(), + onConfirm: (): void => + this.store.dispatch(PlaylistActions.removeAllPlaylists()), }); } } diff --git a/src/app/state/actions.ts b/src/app/state/actions.ts index b9c5e6b40..cfb0ca544 100644 --- a/src/app/state/actions.ts +++ b/src/app/state/actions.ts @@ -115,3 +115,7 @@ export const updatePlaylistPositions = createAction( positionUpdates: { id: string; changes: { position: number } }[]; }>() ); + +export const removeAllPlaylists = createAction( + `${STORE_KEY} Remove all playlists` +); diff --git a/src/app/state/effects.ts b/src/app/state/effects.ts index e4346b9b0..fcc0845a2 100644 --- a/src/app/state/effects.ts +++ b/src/app/state/effects.ts @@ -1,8 +1,10 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { Injectable } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; +import { TranslateService } from '@ngx-translate/core'; import { combineLatestWith, map, switchMap, tap } from 'rxjs/operators'; import { CHANNEL_SET_USER_AGENT, @@ -203,11 +205,32 @@ export class PlaylistEffects { { dispatch: false } ); + removeAll$ = createEffect( + () => { + return this.actions$.pipe( + ofType(PlaylistActions.removeAllPlaylists), + switchMap(() => this.playlistsService.removeAll()), + tap(() => { + this.snackBar.open( + this.translate.instant('SETTINGS.PLAYLISTS_REMOVED'), + null, + { + duration: 2000, + } + ); + }) + ); + }, + { dispatch: false } + ); + constructor( private actions$: Actions, private playlistsService: PlaylistsService, private dataService: DataService, + private router: Router, + private snackBar: MatSnackBar, private store: Store, - private router: Router + private translate: TranslateService ) {} } diff --git a/src/app/state/reducers.ts b/src/app/state/reducers.ts index cb99c3b4c..9eeaebe12 100644 --- a/src/app/state/reducers.ts +++ b/src/app/state/reducers.ts @@ -205,6 +205,12 @@ export const playlistReducer = createReducer( state.playlists ), }; + }), + on(PlaylistActions.removeAllPlaylists, (state): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.removeAll(state.playlists), + }; }) ); diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index c6b0ed4ed..d1fba431d 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -90,16 +90,19 @@ "VERSION_DESCRIPTION": "Current version of the application", "EPG_NOTE": "Currently EPG is available only in the electron-based version of IPTVnator.", "EPG_NOTE_URL_TEXT": "Check it here", - "IMPORT_EXPORT_DATA": "Import/Export playlists", - "IMPORT_EXPORT_DATA_DESCRIPTION": "E2xport and import playlists in the JSON format used within the application.", - "IMPORT_DATA": "Import playlists", - "EXPORT_DATA": "Export playlists", - "REMOVE_ALL": "Remove playlists", + "IMPORT_EXPORT_DATA": "Import/export playlists", + "IMPORT_EXPORT_DATA_DESCRIPTION": "Export or import playlists in the JSON format used within the application.", + "IMPORT_DATA": "Import", + "EXPORT_DATA": "Export", + "REMOVE_ALL": "Remove playlist", + "REMOVE_ALL_BUTTON": "Remove", "REMOVE_ALL_DESCRIPTION": "It will remove all existing playlists from the application.", "REMOVE_DIALOG": { "TITLE": "Remove all playlists", "MESSAGE": "Are you sure you want to delete all playlist from the application?" - } + }, + "IMPORT_ERROR": "Import error, please try with another file.", + "PLAYLISTS_REMOVED": "All playlists were removed." }, "THEMES": { "DARK_THEME": "Dark theme",