diff --git a/.eslintrc.json b/.eslintrc.json index 1a54be027..e1f7d15a7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,7 +11,6 @@ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", - "prettier/@typescript-eslint", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking" ], @@ -38,7 +37,8 @@ "SwitchCase": 1, "CallExpression": { "arguments": "first" }, "FunctionExpression": { "parameters": "first" }, - "FunctionDeclaration": { "parameters": "first" } + "FunctionDeclaration": { "parameters": "first" }, + "ignoredNodes": ["TSTypeParameterInstantiation"] } ], "@typescript-eslint/no-empty-function": 0, @@ -49,6 +49,7 @@ "@typescript-eslint/no-unsafe-assignment": 0, "@typescript-eslint/no-unsafe-return": 0, "@typescript-eslint/no-floating-promises": 0, + "@typescript-eslint/unbound-method": ["off"], "@angular-eslint/use-injectable-provided-in": "error", "@angular-eslint/no-attribute-decorator": "error" } diff --git a/.husky/commit-msg b/.husky/commit-msg index 7ea66c8db..a341454e9 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -6,3 +6,4 @@ npx stylelint "**/*.scss" npx --no-install commitlint --edit npm test -- --coverage +npx eslint src/**/*.ts \ No newline at end of file diff --git a/angular.json b/angular.json index 604168b93..c643a0445 100644 --- a/angular.json +++ b/angular.json @@ -45,23 +45,6 @@ } ] }, - "web": { - "optimization": false, - "outputHashing": "all", - "sourceMap": true, - "extractCss": true, - "namedChunks": false, - "aot": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": false, - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.web.ts" - } - ] - }, "production": { "optimization": true, "outputHashing": "all", diff --git a/api.ts b/api.ts index 096bc05e3..a5a7b394d 100644 --- a/api.ts +++ b/api.ts @@ -1,9 +1,13 @@ +import { ParsedPlaylist } from './src/typings.d'; import { app, BrowserWindow, ipcMain } from 'electron'; import { parse } from 'iptv-playlist-parser'; import axios from 'axios'; import { guid } from '@datorama/akita'; -import { Playlist } from './src/app/home/playlist.interface'; -import Nedb from 'nedb-promises-ts'; +import { + Playlist, + PlaylistUpdateState, +} from './src/app/home/playlist.interface'; +import Nedb, { Cursor } from 'nedb-promises-ts'; import { EPG_ERROR, EPG_FETCH, @@ -36,9 +40,9 @@ export class Api { workerWindow: BrowserWindow; constructor() { - ipcMain.on('parse-playlist-by-url', async (event, args) => { + ipcMain.on('parse-playlist-by-url', (event, args) => { try { - await axios.get(args.url).then((result) => { + axios.get(args.url).then((result) => { const parsedPlaylist = this.convertFileStringToPlaylist( result.data ); @@ -77,55 +81,63 @@ export class Api { ipcMain.on('playlists-all', (event) => this.sendAllPlaylists(event)); - ipcMain.on('playlist-by-id', async (event, args) => { - const playlist = await db.findOne({ _id: args.id }); - this.setUserAgent(playlist.userAgent); - event.sender.send(PLAYLIST_PARSE_RESPONSE, { - payload: playlist, + ipcMain.on('playlist-by-id', (event, args) => { + db.findOne({ _id: args.id }).then((playlist) => { + this.setUserAgent(playlist.userAgent); + event.sender.send(PLAYLIST_PARSE_RESPONSE, { + payload: playlist, + }); }); }); - ipcMain.on('playlist-remove-by-id', async (event, args) => { - const removed = await db.remove({ _id: args.id }); - if (removed) { - event.sender.send('playlist-remove-by-id-result', { - message: 'playlist was removed', - }); - } + ipcMain.on('playlist-remove-by-id', (event, args) => { + db.remove({ _id: args.id }).then((removed) => { + if (removed) { + event.sender.send('playlist-remove-by-id-result', { + message: 'playlist was removed', + }); + } + }); }); // open playlist from file system ipcMain.on('open-file', (event, args) => { - fs.readFile(args.filePath, 'utf-8', (err, data: string) => { - if (err) { - console.log( - 'An error ocurred reading the file :' + err.message - ); - return; - } + fs.readFile( + args.filePath, + 'utf-8', + (err: NodeJS.ErrnoException, data: string) => { + if (err) { + console.log( + 'An error ocurred reading the file :' + err.message + ); + return; + } - const parsedPlaylist = this.convertFileStringToPlaylist(data); - const playlistObject = this.createPlaylistObject( - args.fileName, - parsedPlaylist, - args.filePath, - 'FILE' - ); - this.insertToDb(playlistObject); - event.sender.send(PLAYLIST_PARSE_RESPONSE, { - payload: playlistObject, - }); - }); + const parsedPlaylist = + this.convertFileStringToPlaylist(data); + const playlistObject = this.createPlaylistObject( + args.fileName, + parsedPlaylist, + args.filePath, + 'FILE' + ); + this.insertToDb(playlistObject); + event.sender.send(PLAYLIST_PARSE_RESPONSE, { + payload: playlistObject, + }); + } + ); }); - ipcMain.on('update-favorites', async (event, args) => { - const updated = await db.update( + ipcMain.on('update-favorites', (event, args) => { + db.update( { id: args.id }, { $set: { favorites: args.favorites } } - ); - if (!updated.numAffected || updated.numAffected === 0) { - console.error('Error: Favorites were not updated'); - } + ).then((updated) => { + if (!updated.numAffected || updated.numAffected === 0) { + console.error('Error: Favorites were not updated'); + } + }); }); // listeners for EPG events @@ -146,22 +158,29 @@ export class Api { this.mainWindow.webContents.send(EPG_ERROR, arg) ); - ipcMain.on(PLAYLIST_SAVE_DETAILS, async (event, args) => { - const updated = await db.update( - { _id: args._id }, - { - $set: { - title: args.title, - userAgent: args.userAgent, - autoRefresh: args.autoRefresh, - }, - } - ); - if (!updated.numAffected || updated.numAffected === 0) { - console.error('Error: Playlist details were not updated'); + ipcMain.on( + PLAYLIST_SAVE_DETAILS, + ( + event, + args: Pick< + Playlist, + '_id' | 'title' | 'userAgent' | 'autoRefresh' + > + ) => { + this.updatePlaylistById(args._id, { + title: args.title, + userAgent: args.userAgent, + autoRefresh: args.autoRefresh, + }).then((updated) => { + if (!updated.numAffected || updated.numAffected === 0) { + console.error( + 'Error: Playlist details were not updated' + ); + } + this.sendAllPlaylists(event); + }); } - this.sendAllPlaylists(event); - }); + ); ipcMain.on( PLAYLIST_UPDATE, @@ -173,16 +192,52 @@ export class Api { } } ); + + this.refreshPlaylists(); + } + + /** + * Starts the update process for all the playlists with the enabled auto-refresh flag + */ + refreshPlaylists(): void { + this.getAllPlaylistsMeta().then((playlists) => { + playlists.forEach((playlist) => { + if (playlist.autoRefresh && playlist.autoRefresh === true) { + if (playlist.url) { + this.fetchPlaylistByUrl(playlist._id, playlist.url); + } else if (playlist.filePath) { + this.fetchPlaylistByFilePath( + playlist._id, + playlist.filePath + ); + } else { + console.log('skip...'); + } + } + }); + }); } /** * Sends list with all playlists which are stored in the database * @param event main event */ - async sendAllPlaylists(event: Electron.IpcMainEvent): Promise { - const playlists = await db.find( - { type: { $exists: false } }, - { + sendAllPlaylists(event: Electron.IpcMainEvent): void { + this.getAllPlaylistsMeta().then((playlists) => { + event.sender.send('playlist-all-result', { + payload: playlists, + }); + }); + } + + /** + * Returns all existing playlists with meta information from the database + * @returns + */ + getAllPlaylistsMeta(): Cursor { + return db + .find({ type: { $exists: false } }) + .projection({ count: 1, title: 1, _id: 1, @@ -193,11 +248,9 @@ export class Api { filePath: 1, autoRefresh: 1, updateDate: 1, - } - ); - event.sender.send('playlist-all-result', { - payload: playlists, - }); + updateState: 1, + }) + .sort({ importDate: -1 }); } /** @@ -280,11 +333,33 @@ export class Api { playlist, count: playlist.items.length, updateDate: Date.now(), + updateState: PlaylistUpdateState.UPDATED, }, } ); } + /** + * Updates the provided playlist in the database + * @param id id of the playlist + * @param data playlist data to update + * @returns + */ + updatePlaylistById( + id: string, + data: Partial + ): Promise<{ + numAffected: number; + upsert: boolean; + }> { + return db.update( + { _id: id }, + { + $set: data, + } + ); + } + /** * Converts the fetched playlist string to the playlist object, updates it in the database and sends the updated playlists array back to the renderer * @param id id of the playlist to update @@ -294,20 +369,28 @@ export class Api { async handlePlaylistRefresh( id: string, playlistString: any, - event: Electron.IpcMainEvent + event?: Electron.IpcMainEvent ): Promise { - const playlist = this.convertFileStringToPlaylist(playlistString); - const updated = await this.saveUpdatedPlaylist(id, playlist); + const playlist: ParsedPlaylist = + this.convertFileStringToPlaylist(playlistString); + const updated = await this.updatePlaylistById(id, { + playlist, + count: playlist.items.length, + updateDate: Date.now(), + updateState: PlaylistUpdateState.UPDATED, + }); if (!updated.numAffected || updated.numAffected === 0) { console.error('Error: Playlist details were not updated'); } - event.sender.send(PLAYLIST_UPDATE_RESPONSE, { - message: `Success! The playlist was successfully updated (${playlist.items.length} channels)`, - }); + if (event) { + event.sender.send(PLAYLIST_UPDATE_RESPONSE, { + message: `Success! The playlist was successfully updated (${playlist.items.length} channels)`, + }); - // send all playlists back to the renderer process - this.sendAllPlaylists(event); + // send all playlists back to the renderer process + this.sendAllPlaylists(event); + } } /** @@ -319,7 +402,7 @@ export class Api { async fetchPlaylistByUrl( id: string, url: string, - event: Electron.IpcMainEvent + event?: Electron.IpcMainEvent ): Promise { try { await axios @@ -328,8 +411,11 @@ export class Api { this.handlePlaylistRefresh(id, result.data, event) ); } catch (err) { + this.updatePlaylistById(id, { + updateState: PlaylistUpdateState.NOT_UPDATED, + }); event.sender.send(ERROR, { - message: `${err.response.statusText}. Please check the entered playlist URL again.`, + message: `File not found. Please check the entered playlist URL again.`, status: err.response.status, }); } @@ -344,19 +430,19 @@ export class Api { fetchPlaylistByFilePath( id: string, path: string, - event: Electron.IpcMainEvent + event?: Electron.IpcMainEvent ): void { try { - fs.readFile(path, 'utf-8', async (err, data) => { + fs.readFile(path, 'utf-8', (err, data) => { if (err) { - this.handleFileNotFoundError(err, event); + this.handleFileNotFoundError(err, id, event); return; } this.handlePlaylistRefresh(id, data, event); }); } catch (err) { - this.handleFileNotFoundError(err, event); + this.handleFileNotFoundError(err, id, event); } } @@ -372,13 +458,21 @@ export class Api { syscall: string; path: string; }, - event: Electron.IpcMainEvent + id: string, + event?: Electron.IpcMainEvent ) { console.error(error); - event.sender.send(ERROR, { - message: `Sorry, playlist was not found (${error.path})`, - status: 'ENOENT', + this.updatePlaylistById(id, { + updateState: PlaylistUpdateState.NOT_UPDATED, }); + if (event) { + // send all playlists back to the renderer process + this.sendAllPlaylists(event); + event.sender.send(ERROR, { + message: `Sorry, playlist was not found (${error.path})`, + status: 'ENOENT', + }); + } } convertFileStringToPlaylist(m3uString: string): any { @@ -398,7 +492,7 @@ export class Api { * Inserts new playlist to the database * @param playlist playlist to add */ - insertToDb(playlist: any): void { + insertToDb(playlist: Playlist): void { db.insert(playlist).then((response) => { console.log('playlist was saved...', response._id); }); diff --git a/electron-builder.json b/electron-builder.json index a23ee4693..aa7e7ae82 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -14,8 +14,6 @@ "!e2e/", "!hooks/", "!angular.json", - "!_config.yml", - "!karma.conf.js", "!tsconfig.json", "!tslint.json" ], diff --git a/package.json b/package.json index d32e78569..578931388 100644 --- a/package.json +++ b/package.json @@ -1,157 +1,157 @@ { - "name": "iptvnator", - "version": "0.6.0", - "description": "IPTV player application.", - "homepage": "https://github.com/4gray/iptvnator", - "author": { - "name": "4gray", - "email": "dev@artslab.info" - }, - "keywords": [ - "angular", - "electron", - "typescript", - "m3u", - "m3u8", - "player", - "iptv", - "video", - "tv" - ], - "main": "main.js", - "private": true, - "scripts": { - "postinstall": "electron-builder install-app-deps && husky install", - "ng": "ng", - "start": "npm-run-all -p electron:serve ng:serve", - "build": "npm run electron:serve-tsc && ng build --base-href ./", - "build:dev": "npm run build -- -c dev", - "build:prod": "npm run build -- -c production", - "ng:serve": "ng serve", - "ng:serve:web": "ng serve -c web -o", - "electron:serve-tsc": "tsc -p tsconfig.serve.json", - "electron:serve": "wait-on tcp:4200 && npm run electron:serve-tsc && npx electron . --serve", - "electron:local": "npm run build:prod && npx electron .", - "electron:build": "npm run build:prod && electron-builder build", - "electron:build:mac": "npm run build:prod && electron-builder build -m", - "electron:build:linux": "npm run build:prod && electron-builder build -l --x64 --ia32 --arm64 --armv7l", - "electron:build:windows": "npm run build:prod && electron-builder build -w", - "test": "jest", - "test:watch": "jest --watch", - "e2e": "npm run build:prod && cross-env TS_NODE_PROJECT='e2e/tsconfig.e2e.json' mocha e2e/**/*.ts --config ./.mocharc.json --exit", - "e2e:watch": "npm run e2e -- --watch", - "version": "conventional-changelog -i CHANGELOG.md -p angular -s -r 0 && git add CHANGELOG.md", - "lint": "ng lint" - }, - "devDependencies": { - "@angular-builders/custom-webpack": "9.2.0", - "@angular-devkit/build-angular": "0.1101.4", - "@angular-eslint/builder": "0.0.1-alpha.32", - "@angular-eslint/eslint-plugin": "0.0.1-alpha.32", - "@angular-eslint/eslint-plugin-template": "0.0.1-alpha.32", - "@angular-eslint/template-parser": "0.0.1-alpha.32", - "@angular/cli": "11.1.4", - "@angular/common": "11.1.2", - "@angular/compiler": "11.1.2", - "@angular/compiler-cli": "11.1.2", - "@angular/core": "11.1.2", - "@angular/forms": "11.1.2", - "@angular/language-service": "11.1.2", - "@angular/platform-browser": "11.1.2", - "@angular/platform-browser-dynamic": "11.1.2", - "@angular/router": "11.1.2", - "@commitlint/cli": "11.0.0", - "@commitlint/config-conventional": "11.0.0", - "@ngx-translate/core": "13.0.0", - "@ngx-translate/http-loader": "6.0.0", - "@semantic-release/changelog": "5.0.1", - "@semantic-release/git": "9.0.0", - "@semantic-release/npm": "7.1.1", - "@types/jasmine": "~3.6.0", - "@types/jasminewd2": "2.0.8", - "@types/jest": "26.0.14", - "@types/mocha": "7.0.2", - "@types/node": "12.12.6", - "@typescript-eslint/eslint-plugin": "4.11.1", - "@typescript-eslint/eslint-plugin-tslint": "4.11.1", - "@typescript-eslint/parser": "4.11.1", - "chai": "4.2.0", - "conventional-changelog-cli": "2.0.34", - "core-js": "3.6.5", - "cross-env": "7.0.2", - "electron": "10.2.0", - "electron-builder": "22.9.1", - "eslint": "7.13.0", - "eslint-plugin-import": "2.22.1", - "husky": "5.0.9", - "jasmine-core": "~3.6.0", - "jasmine-spec-reporter": "~5.0.0", - "jest": "26.4.2", - "jest-preset-angular": "8.3.1", - "karma": "5.2.3", - "karma-coverage-istanbul-reporter": "3.0.3", - "karma-electron": "6.3.0", - "karma-jasmine": "~4.0.0", - "karma-jasmine-html-reporter": "^1.5.0", - "mocha": "7.2.0", - "ng-mocks": "11.5.0", - "npm-run-all": "4.1.5", - "prettier": "2.3.0", - "rxjs": "6.6.3", - "semantic-release": "17.2.3", - "spectron": "12.0.0", - "stylelint": "13.13.1", - "stylelint-config-standard": "22.0.0", - "stylelint-scss": "3.19.0", - "ts-node": "8.10.2", - "tslib": "2.1.0", - "typescript": "4.1.3", - "wait-on": "5.2.1", - "webdriver-manager": "12.1.7", - "zone.js": "~0.11.3" - }, - "engines": { - "node": ">=10.13.0" - }, - "dependencies": { - "@angular/animations": "11.1.2", - "@angular/cdk": "11.1.2", - "@angular/flex-layout": "10.0.0-beta.32", - "@angular/material": "11.1.2", - "@datorama/akita": "5.2.6", - "@ngx-pwa/local-storage": "11.1.0", - "@types/hls.js": "0.13.3", - "@types/video.js": "7.3.11", - "@videojs/http-streaming": "2.3.1", - "about-window": "1.13.4", - "axios": "0.21.1", - "custom-electron-titlebar": "3.2.5", - "epg-parser": "0.1.1", - "hls.js": "0.14.16", - "iptv-playlist-parser": "0.6.0", - "lodash": "4.17.21", - "material-icons": "0.3.1", - "moment": "2.29.1", - "nedb": "1.8.0", - "nedb-promises-ts": "0.0.1-4", - "ngx-filter-pipe": "2.1.2", - "ngx-uploader": "11.0.0", - "ngx-whats-new": "0.0.1", - "semver": "7.3.4", - "uuid": "8.3.0", - "video-js": "0.7.0" - }, - "jest": { - "preset": "jest-preset-angular", - "setupFilesAfterEnv": [ - "/src/setupJest.ts" + "name": "iptvnator", + "version": "0.6.0", + "description": "IPTV player application.", + "homepage": "https://github.com/4gray/iptvnator", + "author": { + "name": "4gray", + "email": "dev@artslab.info" + }, + "keywords": [ + "angular", + "electron", + "typescript", + "m3u", + "m3u8", + "player", + "iptv", + "video", + "tv" ], - "testMatch": [ - "**/+(*.)+(spec|test).+(ts)?(x)" - ], - "coverageReporters": [ - "html", - "lcov" - ] - } + "main": "main.js", + "private": true, + "scripts": { + "postinstall": "electron-builder install-app-deps && husky install", + "ng": "ng", + "start": "npm-run-all -p electron:serve ng:serve", + "build": "npm run electron:serve-tsc && ng build --base-href ./", + "build:dev": "npm run build -- -c dev", + "build:prod": "npm run build -- -c production", + "ng:serve": "ng serve", + "ng:serve:web": "ng serve -c web -o", + "electron:serve-tsc": "tsc -p tsconfig.serve.json", + "electron:serve": "wait-on tcp:4200 && npm run electron:serve-tsc && npx electron . --serve", + "electron:local": "npm run build:prod && npx electron .", + "electron:build": "npm run build:prod && electron-builder build", + "electron:build:mac": "npm run build:prod && electron-builder build -m", + "electron:build:linux": "npm run build:prod && electron-builder build -l --x64 --ia32 --arm64 --armv7l", + "electron:build:windows": "npm run build:prod && electron-builder build -w", + "test": "jest", + "test:watch": "jest --watch", + "e2e": "npm run build:prod && cross-env TS_NODE_PROJECT='e2e/tsconfig.e2e.json' mocha e2e/**/*.ts --config ./.mocharc.json --exit", + "e2e:watch": "npm run e2e -- --watch", + "version": "conventional-changelog -i CHANGELOG.md -p angular -s -r 0 && git add CHANGELOG.md", + "lint": "ng lint" + }, + "devDependencies": { + "@angular-builders/custom-webpack": "9.2.0", + "@angular-devkit/build-angular": "0.1101.4", + "@angular-eslint/builder": "0.0.1-alpha.32", + "@angular-eslint/eslint-plugin": "0.0.1-alpha.32", + "@angular-eslint/eslint-plugin-template": "0.0.1-alpha.32", + "@angular-eslint/template-parser": "0.0.1-alpha.32", + "@angular/cli": "11.1.4", + "@angular/common": "11.1.2", + "@angular/compiler": "11.1.2", + "@angular/compiler-cli": "11.1.2", + "@angular/core": "11.1.2", + "@angular/forms": "11.1.2", + "@angular/language-service": "11.1.2", + "@angular/platform-browser": "11.1.2", + "@angular/platform-browser-dynamic": "11.1.2", + "@angular/router": "11.1.2", + "@commitlint/cli": "11.0.0", + "@commitlint/config-conventional": "11.0.0", + "@ngx-translate/core": "13.0.0", + "@ngx-translate/http-loader": "6.0.0", + "@semantic-release/changelog": "5.0.1", + "@semantic-release/git": "9.0.0", + "@semantic-release/npm": "7.1.1", + "@types/jasmine": "~3.6.0", + "@types/jasminewd2": "2.0.8", + "@types/jest": "26.0.14", + "@types/mocha": "7.0.2", + "@types/node": "12.12.6", + "@typescript-eslint/eslint-plugin": "4.11.1", + "@typescript-eslint/eslint-plugin-tslint": "4.11.1", + "@typescript-eslint/parser": "4.11.1", + "chai": "4.2.0", + "conventional-changelog-cli": "2.0.34", + "core-js": "3.6.5", + "cross-env": "7.0.2", + "electron": "10.2.0", + "electron-builder": "22.9.1", + "eslint": "7.13.0", + "eslint-plugin-import": "2.22.1", + "husky": "5.0.9", + "jasmine-core": "~3.6.0", + "jasmine-spec-reporter": "~5.0.0", + "jest": "26.4.2", + "jest-preset-angular": "8.3.1", + "karma": "5.2.3", + "karma-coverage-istanbul-reporter": "3.0.3", + "karma-electron": "6.3.0", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", + "mocha": "7.2.0", + "ng-mocks": "11.5.0", + "npm-run-all": "4.1.5", + "prettier": "2.3.0", + "rxjs": "6.6.3", + "semantic-release": "17.2.3", + "spectron": "12.0.0", + "stylelint": "13.13.1", + "stylelint-config-standard": "22.0.0", + "stylelint-scss": "3.19.0", + "ts-node": "8.10.2", + "tslib": "2.1.0", + "typescript": "4.1.3", + "wait-on": "5.2.1", + "webdriver-manager": "12.1.7", + "zone.js": "~0.11.3" + }, + "engines": { + "node": ">=10.13.0" + }, + "dependencies": { + "@angular/animations": "11.1.2", + "@angular/cdk": "11.1.2", + "@angular/flex-layout": "10.0.0-beta.32", + "@angular/material": "11.1.2", + "@datorama/akita": "5.2.6", + "@ngx-pwa/local-storage": "11.1.0", + "@types/hls.js": "0.13.3", + "@types/video.js": "7.3.11", + "@videojs/http-streaming": "2.3.1", + "about-window": "1.13.4", + "axios": "0.21.1", + "custom-electron-titlebar": "3.2.5", + "epg-parser": "0.1.1", + "hls.js": "0.14.16", + "iptv-playlist-parser": "0.6.0", + "lodash": "4.17.21", + "material-icons": "0.3.1", + "moment": "2.29.1", + "nedb": "1.8.0", + "nedb-promises-ts": "0.0.1-4", + "ngx-filter-pipe": "2.1.2", + "ngx-uploader": "11.0.0", + "ngx-whats-new": "0.0.1", + "semver": "7.3.4", + "uuid": "8.3.0", + "video-js": "0.7.0" + }, + "jest": { + "preset": "jest-preset-angular", + "setupFilesAfterEnv": [ + "/src/setupJest.ts" + ], + "testMatch": [ + "**/+(*.)+(spec|test).+(ts)?(x)" + ], + "coverageReporters": [ + "html", + "lcov" + ] + } } diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index 953b8232e..3c09eaeb7 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -41,18 +41,18 @@ export class HomeComponent { commandsList = [ { id: PLAYLIST_PARSE_RESPONSE, - execute: (response: any) => this.setPlaylist(response.payload), + execute: (response: { payload: Playlist }) => + this.setPlaylist(response.payload), }, { id: 'playlist-all-result', - execute: (response: { payload: Partial }) => - (this.playlists = response.payload.sort((a, b) => - b.importDate.localeCompare(a.importDate) - )), + execute: (response: { payload: Partial }): void => { + this.playlists = response.payload; + }, }, { id: 'playlist-remove-by-id-result', - execute: () => { + execute: (): void => { this.snackBar.open('Done! Playlist was removed.', null, { duration: 2000, }); @@ -61,7 +61,7 @@ export class HomeComponent { }, { id: ERROR, - execute: (response: { message: string; status: number }) => { + execute: (response: { message: string; status: number }): void => { this.isLoading = false; this.showNotification( `Error: ${response.status} ${response.message}.` diff --git a/src/app/home/playlist.interface.ts b/src/app/home/playlist.interface.ts index 9e4e091e5..98ede0f9d 100644 --- a/src/app/home/playlist.interface.ts +++ b/src/app/home/playlist.interface.ts @@ -26,6 +26,6 @@ export interface Playlist { userAgent?: string; filePath?: string; autoRefresh: boolean; - updateDate?: string; + updateDate?: number; updateState?: PlaylistUpdateState; } diff --git a/src/app/home/recent-playlists/playlist-info/playlist-info.component.html b/src/app/home/recent-playlists/playlist-info/playlist-info.component.html index d56c52b73..858fd4a0e 100644 --- a/src/app/home/recent-playlists/playlist-info/playlist-info.component.html +++ b/src/app/home/recent-playlists/playlist-info/playlist-info.component.html @@ -42,6 +42,27 @@ }} + + + {{ 'HOME.PLAYLISTS.INFO_DIALOG.FILE_PATH' | translate }} + + + + {{ 'HOME.PLAYLISTS.INFO_DIALOG.UPDATE_FAILED' | translate }} + + + + {{ 'HOME.PLAYLISTS.INFO_DIALOG.AUTO_UPDATE' | translate }} + +

+ {{ + 'HOME.PLAYLISTS.INFO_DIALOG.AUTO_UPDATE_DESCRIPTION' | translate + }} +