diff --git a/apps/electron-backend/src/app/api/main.preload.ts b/apps/electron-backend/src/app/api/main.preload.ts index e17de49d7..35058aa15 100644 --- a/apps/electron-backend/src/app/api/main.preload.ts +++ b/apps/electron-backend/src/app/api/main.preload.ts @@ -8,8 +8,10 @@ contextBridge.exposeInMainWorld('electron', { updatePlaylistFromFilePath: (filePath: string, title: string) => ipcRenderer.invoke('update-playlist-from-file-path', filePath, title), openPlaylistFromFile: () => ipcRenderer.invoke('open-playlist-from-file'), - saveFileDialog: (defaultPath: string, filters?: { name: string; extensions: string[] }[]) => - ipcRenderer.invoke('save-file-dialog', defaultPath, filters), + saveFileDialog: ( + defaultPath: string, + filters?: { name: string; extensions: string[] }[] + ) => ipcRenderer.invoke('save-file-dialog', defaultPath, filters), writeFile: (filePath: string, content: string) => ipcRenderer.invoke('write-file', filePath, content), setUserAgent: (userAgent: string, referer?: string) => diff --git a/apps/electron-backend/src/app/app.ts b/apps/electron-backend/src/app/app.ts index 1e9bdd976..6bd03b797 100644 --- a/apps/electron-backend/src/app/app.ts +++ b/apps/electron-backend/src/app/app.ts @@ -78,6 +78,8 @@ export default class App { preload: join(__dirname, 'main.preload.js'), }, ...savedWindowBounds, + minHeight: 600, + minWidth: 900, }); App.mainWindow.setMenu(null); if (!savedWindowBounds) { diff --git a/apps/electron-backend/src/app/events/player.events.ts b/apps/electron-backend/src/app/events/player.events.ts index 7f1f221b0..05439d8a6 100644 --- a/apps/electron-backend/src/app/events/player.events.ts +++ b/apps/electron-backend/src/app/events/player.events.ts @@ -90,7 +90,10 @@ ipcMain.handle( ); return; } catch (err) { - console.error('Failed to send command to existing MPV:', err); + console.error( + 'Failed to send command to existing MPV:', + err + ); // If it fails, clear the reference and create a new one mpvProcess = null; mpvSocketPath = null; diff --git a/apps/electron-backend/src/app/events/xtream.events.ts b/apps/electron-backend/src/app/events/xtream.events.ts index feac61751..f53e366f4 100644 --- a/apps/electron-backend/src/app/events/xtream.events.ts +++ b/apps/electron-backend/src/app/events/xtream.events.ts @@ -5,7 +5,6 @@ import axios, { AxiosRequestConfig } from 'axios'; import { ipcMain } from 'electron'; -import { XTREAM_REQUEST } from 'shared-interfaces'; export default class XtreamEvents { static bootstrapXtreamEvents(): Electron.IpcMain { @@ -17,7 +16,7 @@ export default class XtreamEvents { * Handle Xtream Codes API requests */ ipcMain.handle( - XTREAM_REQUEST, + 'XTREAM_REQUEST', async ( event, payload: { @@ -77,7 +76,11 @@ ipcMain.handle( status: error.response?.status || 500, }; throw errorResponse; - } else if (error && typeof error === 'object' && 'message' in error) { + } else if ( + error && + typeof error === 'object' && + 'message' in error + ) { throw error; } else { throw { diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 7925c5f17..07472a9c3 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -1,6 +1,5 @@ -import { Component, inject, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { MatSnackBar } from '@angular/material/snack-bar'; import { Router, RouterOutlet } from '@angular/router'; import { Store } from '@ngrx/store'; import { TranslateService } from '@ngx-translate/core'; @@ -8,15 +7,11 @@ import { TranslateService } from '@ngx-translate/core'; import * as PlaylistActions from 'm3u-state'; import { DataService, EpgService } from 'services'; import { - ERROR, - IpcCommand, Language, OPEN_FILE, Settings, STORE_KEY, Theme, - VIEW_ADD_PLAYLIST, - VIEW_SETTINGS, } from 'shared-interfaces'; import { SettingsService } from './services/settings.service'; import { RecentlyViewedComponent } from './xtream-tauri/recently-viewed/recently-viewed.component'; @@ -27,30 +22,24 @@ import { SearchResultsComponent } from './xtream-tauri/search-results/search-res templateUrl: './app.component.html', imports: [RouterOutlet], }) -export class AppComponent implements OnInit, OnDestroy { +export class AppComponent implements OnInit { private dataService = inject(DataService); private dialog = inject(MatDialog); private epgService = inject(EpgService); private router = inject(Router); private store = inject(Store); - private snackBar = inject(MatSnackBar); private translate = inject(TranslateService); private settingsService = inject(SettingsService); /** List of ipc commands with function mapping */ - private readonly commandsList = [ + /* private readonly commandsList = [ new IpcCommand(VIEW_ADD_PLAYLIST, () => this.navigateToRoute('/')), new IpcCommand(VIEW_SETTINGS, () => this.navigateToRoute('/settings')), - new IpcCommand(ERROR, (response: { message: string; status: number }) => - this.showErrorAsNotification(response) - ), - ]; + ]; */ /** Default language as fallback */ private readonly DEFAULT_LANG = Language.ENGLISH; - private listeners = []; - constructor() { if ( ((this.dataService.isElectron && @@ -90,24 +79,7 @@ export class AppComponent implements OnInit, OnDestroy { this.store.dispatch(PlaylistActions.loadPlaylists()); this.translate.setDefaultLang(this.DEFAULT_LANG); - this.setRendererListeners(); this.initSettings(); - /* this.handleWhatsNewDialog(); */ - } - - /** - * Initializes all necessary listeners for the events from the renderer process - */ - setRendererListeners(): void { - this.commandsList.forEach((command) => { - const cb = (response) => { - if (response.data.type === command.id) { - command.callback(response.data); - } - }; - this.dataService.listenOn(command.id, cb); - this.listeners.push(cb); - }); } /** @@ -134,6 +106,8 @@ export class AppComponent implements OnInit, OnDestroy { this.epgService.fetchEpg(settings.epgUrl); } + // TODO: trigger auto-refresh mechanism for playlists + if (settings.theme) { this.settingsService.changeTheme(settings.theme); } else { @@ -160,21 +134,6 @@ export class AppComponent implements OnInit, OnDestroy { } } - /** - * Checks the actual version of the application and shows the "what is new" dialog if the updated version was detected - */ - /* handleWhatsNewDialog(): void { - const actualVersion = this.dataService.getAppVersion(); - this.settingsService - .getValueFromLocalStorage(STORE_KEY.Version) - .subscribe(() => { - this.settingsService.setValueToLocalStorage( - STORE_KEY.Version, - actualVersion - ); - }); - } */ - /** * Navigate to the specified route * @param route route to navigate to @@ -183,16 +142,6 @@ export class AppComponent implements OnInit, OnDestroy { this.router.navigateByUrl(route); } - showErrorAsNotification(response: { message: string; status: number }) { - this.snackBar.open( - `Error: ${response?.message ?? 'Something went wrong'} (Status: ${ - response?.status ?? 0 - })`, - null, - { duration: 4000 } - ); - } - openGlobalSearch(): void { this.dialog.open(SearchResultsComponent, { width: '100%', @@ -214,19 +163,4 @@ export class AppComponent implements OnInit, OnDestroy { disableClose: false, }); } - - /** - * Removes all ipc command listeners on component destroy - */ - ngOnDestroy(): void { - if (this.dataService.isElectron) { - this.commandsList.forEach((command) => - this.dataService.removeAllListeners(command.id) - ); - } else { - this.listeners.forEach((listener) => - window.removeEventListener('message', listener) - ); - } - } } diff --git a/apps/web/src/app/home/home.component.html b/apps/web/src/app/home/home.component.html index 8d2c48dbe..a7574a9b1 100644 --- a/apps/web/src/app/home/home.component.html +++ b/apps/web/src/app/home/home.component.html @@ -1,10 +1,5 @@ -
- - - @if (isLoading) { - - } -
+/> + diff --git a/apps/web/src/app/home/home.component.scss b/apps/web/src/app/home/home.component.scss index 39151a654..72585c820 100644 --- a/apps/web/src/app/home/home.component.scss +++ b/apps/web/src/app/home/home.component.scss @@ -1,24 +1,4 @@ -::ng-deep { - .mat-tab-label-content { - text-transform: uppercase; - } - - .mat-tab-group.mat-background-primary .mat-tab-header-pagination { - display: none; - } -} - -.playlists-container { - grid-area: playlists; -} - .recent-playlists { height: calc(100vh - 100px); overflow: auto; } - -@media only screen and (max-width: 480px) { - .tab-title { - display: none; - } -} diff --git a/apps/web/src/app/home/home.component.ts b/apps/web/src/app/home/home.component.ts index c751531f7..c46fe0596 100644 --- a/apps/web/src/app/home/home.component.ts +++ b/apps/web/src/app/home/home.component.ts @@ -1,103 +1,12 @@ -import { Component, inject, OnDestroy } from '@angular/core'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatSnackBar } from '@angular/material/snack-bar'; -import { Store } from '@ngrx/store'; +import { Component } from '@angular/core'; import { TranslatePipe } from '@ngx-translate/core'; import { RecentPlaylistsComponent } from 'components'; -import { addPlaylist } from 'm3u-state'; -import { DataService } from 'services'; -import { ERROR, Playlist, PLAYLIST_PARSE_RESPONSE } from 'shared-interfaces'; import { HeaderComponent } from '../shared/components/header/header.component'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.scss'], - imports: [ - HeaderComponent, - MatProgressBarModule, - RecentPlaylistsComponent, - TranslatePipe, - ], + imports: [HeaderComponent, RecentPlaylistsComponent, TranslatePipe], }) -export class HomeComponent implements OnDestroy { - private dataService = inject(DataService); - private snackBar = inject(MatSnackBar); - private readonly store = inject(Store); - - /** Loading spinner state */ - isLoading = false; - - /** IPC Renderer commands list with callbacks */ - commandsList = [ - { - id: PLAYLIST_PARSE_RESPONSE, - execute: (response: { payload: Playlist }): void => { - this.store.dispatch( - addPlaylist({ - playlist: response.payload, - }) - ); - }, - }, - { - id: ERROR, - execute: (error: { message: string; status: string }) => { - this.showNotification('Error: ' + error.message); - }, - }, - ]; - - listeners = []; - - constructor() { - this.setRendererListeners(); - } - - /** - * Set electrons main process listeners - */ - setRendererListeners(): void { - this.commandsList.forEach((command) => { - /* if (this.dataService.isElectron) { - this.dataService.listenOn(command.id, (event, response) => - this.ngZone.run(() => command.execute(response)) - ); - } else { */ - const cb = (response) => { - if (response.data.type === command.id) { - command.execute(response.data); - } - }; - this.dataService.listenOn(command.id, cb); - this.listeners.push(cb); - /* } */ - }); - } - - /** - * Shows snack bar notification with a given message - * @param message message to show - * @param duration visibility duration of the snackbar - */ - showNotification(message: string, duration = 2000): void { - this.snackBar.open(message, null, { - duration, - }); - } - - /** - * Remove ipcRenderer listeners after component destroy - */ - ngOnDestroy(): void { - if (this.dataService.isElectron) { - this.commandsList.forEach((command) => - this.dataService.removeAllListeners(command.id) - ); - } else { - this.listeners.forEach((listener) => { - window.removeEventListener('message', listener); - }); - } - } -} +export class HomeComponent {} diff --git a/apps/web/src/app/home/video-player/video-player.component.html b/apps/web/src/app/home/video-player/video-player.component.html index bead9ef46..a6880df8b 100644 --- a/apps/web/src/app/home/video-player/video-player.component.html +++ b/apps/web/src/app/home/video-player/video-player.component.html @@ -96,7 +96,7 @@ } } diff --git a/apps/web/src/app/home/video-player/video-player.component.ts b/apps/web/src/app/home/video-player/video-player.component.ts index 8b9e635f5..c5f2a956b 100644 --- a/apps/web/src/app/home/video-player/video-player.component.ts +++ b/apps/web/src/app/home/video-player/video-player.component.ts @@ -1,17 +1,9 @@ import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; import { AsyncPipe, CommonModule } from '@angular/common'; -import { - Component, - Injector, - NgZone, - OnInit, - effect, - inject, -} from '@angular/core'; +import { Component, Injector, OnInit, effect, inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { MatSidenavModule } from '@angular/material/sidenav'; -import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { Store } from '@ngrx/store'; import { StorageMap } from '@ngx-pwa/local-storage'; @@ -37,10 +29,7 @@ import { DataService, PlaylistsService } from 'services'; import { COMPONENT_OVERLAY_REF, Channel, - ERROR, PLAYLIST_PARSE_BY_URL, - PLAYLIST_PARSE_RESPONSE, - Playlist, STORE_KEY, Settings, SidebarView, @@ -51,37 +40,35 @@ import { SettingsComponent } from '../../settings/settings.component'; @Component({ imports: [ + ArtPlayerComponent, AsyncPipe, AudioPlayerComponent, - InfoOverlayComponent, CommonModule, EpgListComponent, HtmlVideoPlayerComponent, + InfoOverlayComponent, MatSidenavModule, RouterLink, SidebarComponent, ToolbarComponent, VjsPlayerComponent, - ArtPlayerComponent, ], templateUrl: './video-player.component.html', styleUrl: './video-player.component.scss', }) export class VideoPlayerComponent implements OnInit { - private activatedRoute = inject(ActivatedRoute); - private dataService = inject(DataService); + private readonly activatedRoute = inject(ActivatedRoute); + private readonly dataService = inject(DataService); private readonly dialog = inject(MatDialog); - private ngZone = inject(NgZone); - private overlay = inject(Overlay); - private playlistsService = inject(PlaylistsService); - private router = inject(Router); - private settingsStore = inject(SettingsStore); - private snackBar = inject(MatSnackBar); - private storage = inject(StorageMap); - private store = inject(Store); + private readonly overlay = inject(Overlay); + private readonly playlistsService = inject(PlaylistsService); + private readonly router = inject(Router); + private readonly settingsStore = inject(SettingsStore); + private readonly storage = inject(StorageMap); + private readonly store = inject(Store); /** Active selected channel */ - activeChannel$ = this.store + readonly activeChannel$ = this.store .select(selectActive) .pipe(filter((channel) => Boolean((channel as Channel)?.url))); @@ -89,7 +76,7 @@ export class VideoPlayerComponent implements OnInit { channels$!: Observable; /** Current epg program */ - epgProgram$ = this.store.select(selectCurrentEpgProgram); + readonly epgProgram$ = this.store.select(selectCurrentEpgProgram); /** Selected video player options */ playerSettings: Partial = { @@ -97,37 +84,6 @@ export class VideoPlayerComponent implements OnInit { showCaptions: false, }; - /** IPC Renderer commands list with callbacks */ - commandsList = [ - { - id: ERROR, - execute: (response: { message: string }): void => { - this.snackBar.open(response.message, '', { - duration: 3100, - }); - }, - }, - { - id: PLAYLIST_PARSE_RESPONSE, - execute: (response: { payload: Playlist }): void => { - if (response.payload.isTemporary) { - this.store.dispatch( - PlaylistActions.setChannels({ - channels: response.payload.playlist.items, - }) - ); - } else { - this.store.dispatch( - PlaylistActions.addPlaylist({ - playlist: response.payload, - }) - ); - } - this.sidebarView = 'CHANNELS'; - }, - }, - ]; - readonly isDesktop = !!window['electron']; sidebarView: SidebarView = 'CHANNELS'; @@ -158,7 +114,6 @@ export class VideoPlayerComponent implements OnInit { */ ngOnInit(): void { this.applySettings(); - this.setRendererListeners(); this.getPlaylistUrlAsParam(); this.channels$ = this.activatedRoute.params.pipe( @@ -214,23 +169,6 @@ export class VideoPlayerComponent implements OnInit { } } - setRendererListeners(): void { - this.commandsList.forEach((command) => { - if (this.isDesktop) { - this.dataService.listenOn(command.id, (event, response) => - this.ngZone.run(() => command.execute(response)) - ); - } else { - const cb = (response: any) => { - if (response.data.type === command.id) { - command.execute(response.data); - } - }; - this.dataService.listenOn(command.id, cb); - } - }); - } - /** * Reads the app configuration from the browsers storage and applies the settings in the current component */ diff --git a/apps/web/src/app/services/electron.service.ts b/apps/web/src/app/services/electron.service.ts index 8033e6619..dc8dd1e40 100644 --- a/apps/web/src/app/services/electron.service.ts +++ b/apps/web/src/app/services/electron.service.ts @@ -1,16 +1,16 @@ import { inject, Injectable } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Params } from '@angular/router'; +import { Store } from '@ngrx/store'; +import { TranslateService } from '@ngx-translate/core'; +import * as PlaylistActions from 'm3u-state'; import { DataService } from 'services'; import { AUTO_UPDATE_PLAYLISTS, - AUTO_UPDATE_PLAYLISTS_RESPONSE, ERROR, Playlist, PLAYLIST_PARSE_BY_URL, - PLAYLIST_PARSE_RESPONSE, PLAYLIST_UPDATE, - PLAYLIST_UPDATE_RESPONSE, XTREAM_RESPONSE, } from 'shared-interfaces'; import { AppConfig } from '../../environments/environment'; @@ -20,7 +20,9 @@ import { AppConfig } from '../../environments/environment'; }) export class ElectronService extends DataService { private eventListeners: { [key: string]: () => void } = {}; - private snackBar = inject(MatSnackBar); + private readonly snackBar = inject(MatSnackBar); + private readonly store = inject(Store); + private readonly translateService = inject(TranslateService); constructor() { super(); @@ -95,27 +97,18 @@ export class ElectronService extends DataService { } else if (type === AUTO_UPDATE_PLAYLISTS) { const data = payload as Playlist[]; const playlists = await window.electron.autoUpdatePlaylists(data); - /* for await (const item of data) { - if (item.filePath) { - const playlist = await readTextFile(item.filePath); - const parsedPlaylist = parse(playlist); - const playlistObject = createPlaylistObject( - item.title, - parsedPlaylist, - item.filePath, - 'FILE' - ); - playlists.push({ ...playlistObject, _id: item._id }); - } - } - */ - window.postMessage({ - type: AUTO_UPDATE_PLAYLISTS_RESPONSE, - payload: { - message: 'Success! The playlists were successfully updated', + this.store.dispatch( + PlaylistActions.updateManyPlaylists({ playlists, - }, - }); + }) + ); + this.snackBar.open( + this.translateService.instant( + 'HOME.PLAYLISTS.AUTO_REFRESH_UPDATE_SUCCESS' + ), + null, + { duration: 2000 } + ); } else { console.log('Unknown type', type); } @@ -130,25 +123,27 @@ export class ElectronService extends DataService { // Use Electron IPC to make the Stalker request const response = await window.electron.stalkerRequest(payload); return response; - } catch (err: any) { + } catch (err) { console.error('Stalker request error:', err); - window.postMessage({ - type: ERROR, - message: err.message ?? 'Error: not found', - status: err.status ?? 404, - }); + this.snackBar.open( + `Error: ${err.message ?? ' Not found'}, status: ${err.status ?? 404}`, + 'Close', + { + duration: 5000, + } + ); throw err; } } private async fetchM3uPlaylistFromUrl(payload: Partial) { - console.log(payload); window.electron.fetchPlaylistByUrl(payload.url).then((result) => { - // TODO: call store and decide and store where to store based on the isTemporary flag etc - window.postMessage({ - type: PLAYLIST_PARSE_RESPONSE, - payload: { ...result, isTemporary: payload.isTemporary }, - }); + this.store.dispatch( + PlaylistActions.handleAddingPlaylistByUrl({ + isTemporary: !!payload?.isTemporary, + playlist: result, + }) + ); }); } @@ -179,16 +174,23 @@ export class ElectronService extends DataService { } methodToCall.then((playlistObject) => { - window.postMessage({ - type: PLAYLIST_UPDATE_RESPONSE, - payload: { - message: 'Success! The playlist was successfully updated', + this.store.dispatch( + PlaylistActions.updatePlaylist({ playlist: { ...playlistObject, _id: data.id, }, - }, - }); + playlistId: data.id, + }) + ); + + this.snackBar.open( + this.translateService.instant( + 'HOME.PLAYLISTS.PLAYLIST_UPDATE_SUCCESS' + ), + null, + { duration: 2000 } + ); }); } @@ -224,14 +226,21 @@ export class ElectronService extends DataService { window.postMessage(result); return result; } catch (error: any) { - console.error('Xtream request error:', error); - const result = { + console.error('Xtream request error:', error.message); + this.snackBar.open( + `Error: ${error.message ?? 'Failed to connect to Xtream server'}, status: ${ + error.status ?? 500 + }`, + 'Close', + { + duration: 5000, + } + ); + return { type: ERROR, status: error.status ?? 500, message: error.message ?? 'Failed to connect to Xtream server', }; - window.postMessage(result); - return result; } } diff --git a/apps/web/src/app/services/pwa.service.ts b/apps/web/src/app/services/pwa.service.ts index 6ff2b3e86..000bfdda8 100644 --- a/apps/web/src/app/services/pwa.service.ts +++ b/apps/web/src/app/services/pwa.service.ts @@ -12,7 +12,6 @@ import { ERROR, Playlist, PLAYLIST_PARSE_BY_URL, - PLAYLIST_PARSE_RESPONSE, PLAYLIST_UPDATE, STALKER_REQUEST, XTREAM_REQUEST, @@ -88,9 +87,15 @@ export class PwaService extends DataService { this.getPlaylistFromUrl(payload.url) .pipe( catchError((error) => { - window.postMessage({ - type: ERROR, - }); + this.snackBar.open( + `Error: ${error.message ?? 'Unknown error'}, status: ${ + error.status ?? 500 + }`, + 'Close', + { + duration: 5000, + } + ); return throwError(() => error); }) ) @@ -120,19 +125,23 @@ export class PwaService extends DataService { this.getPlaylistFromUrl(payload.url) .pipe( catchError((error) => { - window.postMessage({ - type: ERROR, - message: this.getErrorMessageByStatusCode(error.status), - status: error.status, - }); + this.snackBar.open( + this.getErrorMessageByStatusCode(error.status), + 'Close', + { + duration: 5000, + } + ); return throwError(() => error); }) ) - .subscribe((response: any) => { - window.postMessage({ - type: PLAYLIST_PARSE_RESPONSE, - payload: { ...response, isTemporary: payload.isTemporary }, - }); + .subscribe((response: Playlist) => { + this.store.dispatch( + PlaylistActions.handleAddingPlaylistByUrl({ + isTemporary: !!payload?.isTemporary, + playlist: response, + }) + ); }); } @@ -196,11 +205,15 @@ export class PwaService extends DataService { return result; } catch (error: any) { if (payload.params.action === 'get_account_info') return; - window.postMessage({ - type: ERROR, - status: error.error?.status, - message: error.error?.message ?? 'Unknown error', - }); + this.snackBar.open( + `Error: ${error.message ?? ' Unknown error'}, status: ${ + error.status ?? 500 + }`, + 'Close', + { + duration: 5000, + } + ); } } @@ -232,11 +245,14 @@ export class PwaService extends DataService { return (await response.json()).payload; } catch (err) { console.error('Stalker request error:', err); - window.postMessage({ - type: ERROR, - message: err.message ?? 'Error: not found', - status: err.status ?? 404, - }); + + this.snackBar.open( + `Error: ${err.message ?? ' Not found'}, status: ${err.status ?? 404}`, + 'Close', + { + duration: 5000, + } + ); throw err; } } diff --git a/apps/web/src/app/xtream-tauri/category-view/category-view.component.scss b/apps/web/src/app/xtream-tauri/category-view/category-view.component.scss index 93fd187fc..2f9aa6c33 100644 --- a/apps/web/src/app/xtream-tauri/category-view/category-view.component.scss +++ b/apps/web/src/app/xtream-tauri/category-view/category-view.component.scss @@ -14,7 +14,6 @@ &.selected { background-color: rgba(63, 81, 181, 0.1); - border-left: 4px solid #3f51b5; margin-left: 4px; } } diff --git a/apps/web/src/app/xtream-tauri/playlist-error-view/playlist-error-view.component.html b/apps/web/src/app/xtream-tauri/playlist-error-view/playlist-error-view.component.html index 1902cdecb..fa616be16 100644 --- a/apps/web/src/app/xtream-tauri/playlist-error-view/playlist-error-view.component.html +++ b/apps/web/src/app/xtream-tauri/playlist-error-view/playlist-error-view.component.html @@ -1,20 +1,21 @@