diff --git a/apps/web/src/app/services/workspace-shell-actions.service.ts b/apps/web/src/app/services/workspace-shell-actions.service.ts index d1317fa95..df3b22bd0 100644 --- a/apps/web/src/app/services/workspace-shell-actions.service.ts +++ b/apps/web/src/app/services/workspace-shell-actions.service.ts @@ -1,10 +1,10 @@ import { inject, Injectable, Provider } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; -import { PlaylistType } from '@iptvnator/playlist/shared/ui'; import { WORKSPACE_SHELL_ACTIONS, WorkspaceAccountInfoData, + WorkspacePlaylistType, WorkspaceShellActions, } from '@iptvnator/workspace/shell/util'; @@ -13,12 +13,12 @@ export class AppWorkspaceShellActionsService implements WorkspaceShellActions { private readonly dialog = inject(MatDialog); private readonly router = inject(Router); - openAddPlaylistDialog(type: PlaylistType): void { + openAddPlaylistDialog(type?: WorkspacePlaylistType): void { void import('@iptvnator/playlist/import/feature').then( ({ AddPlaylistDialogComponent }) => { this.dialog.open(AddPlaylistDialogComponent, { width: '600px', - data: { type }, + data: type ? { type } : {}, }); } ); diff --git a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html index f4589e31e..d2d2debca 100644 --- a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html +++ b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html @@ -1,20 +1,123 @@

- {{ 'HOME.URL_UPLOAD.ADD_PLAYLIST' | translate }}: {{ playlistType }} + {{ 'HOME.URL_UPLOAD.ADD_PLAYLIST' | translate }}

- @if (playlistType === 'file') { - - } @else if (playlistType === 'url') { - - } @else if (playlistType === 'text') { - - } @else if (playlistType === 'stalker') { - - } @else if (playlistType === 'xtream') { - + + M3U + Xtream + Stalker + + + @if (category() === 'm3u') { + + + {{ 'HOME.TABS.URL_UPLOAD' | translate }} + + + {{ 'HOME.TABS.FILE_UPLOAD' | translate }} + + + {{ 'HOME.TABS.TEXT_IMPORT' | translate }} + + } + +
+ @switch (playlistType()) { + @case ('file') { + + } + @case ('url') { + + } + @case ('text') { + + } + @case ('stalker') { + + } + @case ('xtream') { + + } + } +
+ + @switch (playlistType()) { + @case ('url') { + + } + @case ('text') { + + } + @case ('xtream') { + + + + } + @case ('stalker') { + @if (stalkerImport()?.isLoading()) { + + } @else { + + } + } + } + diff --git a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.scss b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.scss new file mode 100644 index 000000000..3a0853f0d --- /dev/null +++ b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.scss @@ -0,0 +1,44 @@ +@use '@angular/material' as mat; + +// Compact density themes scoped to this dialog only +// Scale -1 for form fields: 52px height, labels still visible +$form-field-theme: mat.define-theme(( + density: (scale: -1), +)); + +// Scale -3 for button toggles: 36px height +$toggle-theme: mat.define-theme(( + density: (scale: -3), +)); + +:host { + @include mat.form-field-density($form-field-theme); + @include mat.button-toggle-density($toggle-theme); +} + +// ─── Dialog title ──────────────────────────────────────────────────────────── +h2[mat-dialog-title] { + margin-bottom: 8px; +} + +// ─── Toggle groups ─────────────────────────────────────────────────────────── +.category-toggle, +.subtype-toggle { + display: flex; + width: 100%; + margin-bottom: 8px; + + mat-button-toggle { + flex: 1; + --mat-standard-button-toggle-label-text-size: 13px; + } +} + +.subtype-toggle mat-button-toggle { + --mat-standard-button-toggle-label-text-size: 12px; +} + +// ─── Form area — stable height across all views ───────────────────────────── +.form-area { + min-height: 0; +} diff --git a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts index 0fcda8555..adeac8169 100644 --- a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts +++ b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts @@ -1,5 +1,6 @@ -import { Component, inject } from '@angular/core'; +import { Component, computed, inject, signal, viewChild } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MAT_DIALOG_DATA, MatDialogModule, @@ -9,6 +10,10 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { Store } from '@ngrx/store'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { PlaylistType } from '@iptvnator/playlist/shared/ui'; +import { + M3uSubType, + PlaylistCategory, +} from '@iptvnator/workspace/shell/util'; import { PlaylistActions } from 'm3u-state'; import { getFilenameFromUrl } from 'm3u-utils'; import { DataService } from 'services'; @@ -23,6 +28,7 @@ import { XtreamCodeImportComponent } from '../xtream-code-import/xtream-code-imp imports: [ FileUploadComponent, MatButtonModule, + MatButtonToggleModule, MatDialogModule, StalkerPortalImportComponent, TextImportComponent, @@ -32,6 +38,7 @@ import { XtreamCodeImportComponent } from '../xtream-code-import/xtream-code-imp ], selector: 'app-add-playlist', templateUrl: './add-playlist-dialog.component.html', + styleUrl: './add-playlist-dialog.component.scss', }) export class AddPlaylistDialogComponent { private dataService = inject(DataService); @@ -39,12 +46,40 @@ export class AddPlaylistDialogComponent { private store = inject(Store); private snackBar = inject(MatSnackBar); private translateService = inject(TranslateService); - readonly data = inject<{ type: PlaylistType }>(MAT_DIALOG_DATA); + private data = inject<{ type?: PlaylistType } | null>(MAT_DIALOG_DATA, { + optional: true, + }); - readonly playlistType!: PlaylistType; + readonly urlUpload = viewChild(UrlUploadComponent); + readonly textImport = viewChild(TextImportComponent); + readonly xtreamImport = viewChild(XtreamCodeImportComponent); + readonly stalkerImport = viewChild(StalkerPortalImportComponent); + + readonly category = signal('m3u'); + readonly m3uSubType = signal('url'); + + readonly playlistType = computed(() => { + const cat = this.category(); + if (cat === 'xtream') return 'xtream'; + if (cat === 'stalker') return 'stalker'; + return this.m3uSubType(); + }); constructor() { - this.playlistType = this.data.type; + if (this.data?.type) { + this.initFromType(this.data.type); + } + } + + private initFromType(type: PlaylistType): void { + if (type === 'xtream') { + this.category.set('xtream'); + } else if (type === 'stalker') { + this.category.set('stalker'); + } else { + this.category.set('m3u'); + this.m3uSubType.set(type as M3uSubType); + } } /** diff --git a/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.html b/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.html index e371f3a63..e43582867 100644 --- a/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.html +++ b/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.html @@ -118,19 +118,4 @@ }} --> - @if (isLoading()) { -
- - {{ 'HOME.STALKER_PORTAL.VALIDATING' | translate }} -
- } @else { - - } diff --git a/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.ts b/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.ts index 198f31ec2..145136450 100644 --- a/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.ts +++ b/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.ts @@ -6,10 +6,8 @@ import { ReactiveFormsModule, Validators, } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Store } from '@ngrx/store'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; @@ -24,10 +22,8 @@ import { v4 as uuid } from 'uuid'; @Component({ imports: [ FormsModule, - MatButtonModule, MatFormFieldModule, MatInputModule, - MatProgressSpinnerModule, ReactiveFormsModule, TranslatePipe, ], diff --git a/libs/playlist/import/feature/src/lib/text-import/text-import.component.html b/libs/playlist/import/feature/src/lib/text-import/text-import.component.html index c6fd2495e..59bc1261d 100644 --- a/libs/playlist/import/feature/src/lib/text-import/text-import.component.html +++ b/libs/playlist/import/feature/src/lib/text-import/text-import.component.html @@ -1,4 +1,4 @@ -
+ {{ 'HOME.TEXT_IMPORT.LABEL' | translate }} -
diff --git a/libs/playlist/import/feature/src/lib/text-import/text-import.component.ts b/libs/playlist/import/feature/src/lib/text-import/text-import.component.ts index 66ccd8e63..16b92592e 100644 --- a/libs/playlist/import/feature/src/lib/text-import/text-import.component.ts +++ b/libs/playlist/import/feature/src/lib/text-import/text-import.component.ts @@ -5,7 +5,6 @@ import { ReactiveFormsModule, Validators, } from '@angular/forms'; -import { MatButton } from '@angular/material/button'; import { MatInputModule } from '@angular/material/input'; import { TranslatePipe } from '@ngx-translate/core'; @@ -13,7 +12,7 @@ import { TranslatePipe } from '@ngx-translate/core'; selector: 'app-text-import', templateUrl: './text-import.component.html', styleUrls: ['./text-import.component.scss'], - imports: [MatButton, MatInputModule, ReactiveFormsModule, TranslatePipe], + imports: [MatInputModule, ReactiveFormsModule, TranslatePipe], }) export class TextImportComponent { @Output() textAdded = new EventEmitter(); diff --git a/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.html b/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.html index a71596038..e03bf5c10 100644 --- a/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.html +++ b/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.html @@ -1,4 +1,4 @@ -
+ } - diff --git a/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.ts b/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.ts index 29020ad95..752327d99 100644 --- a/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.ts +++ b/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.ts @@ -5,14 +5,13 @@ import { ReactiveFormsModule, Validators, } from '@angular/forms'; -import { MatButton } from '@angular/material/button'; import { MatInputModule } from '@angular/material/input'; import { TranslatePipe } from '@ngx-translate/core'; @Component({ selector: 'app-url-upload', templateUrl: './url-upload.component.html', - imports: [MatButton, MatInputModule, ReactiveFormsModule, TranslatePipe], + imports: [MatInputModule, ReactiveFormsModule, TranslatePipe], }) export class UrlUploadComponent implements OnInit { private readonly fb = inject(FormBuilder); diff --git a/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.html b/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.html index d57ffe211..897bb428c 100644 --- a/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.html +++ b/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.html @@ -34,34 +34,6 @@
-
-
- - -
- -
- @if (connectionStatus) {
diff --git a/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.ts b/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.ts index 8f80a14a8..6ffce9a43 100644 --- a/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.ts +++ b/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.ts @@ -6,7 +6,6 @@ import { ReactiveFormsModule, Validators, } from '@angular/forms'; -import { MatButton } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIcon } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; @@ -20,7 +19,6 @@ import { v4 as uuid } from 'uuid'; @Component({ imports: [ FormsModule, - MatButton, MatFormFieldModule, MatIcon, MatInputModule, @@ -64,10 +62,6 @@ import { v4 as uuid } from 'uuid'; gap: 8px; } - .button-row { - display: flex; - justify-content: space-between; - } `, ], }) diff --git a/libs/playlist/shared/ui/src/index.ts b/libs/playlist/shared/ui/src/index.ts index b72dffa28..0e0c7cc73 100644 --- a/libs/playlist/shared/ui/src/index.ts +++ b/libs/playlist/shared/ui/src/index.ts @@ -1,4 +1,4 @@ -export * from './lib/add-playlist-menu/add-playlist-menu.component'; +export * from './lib/add-playlist-menu/playlist-type'; export * from './lib/filter-sort-menu/filter-sort-menu.component'; export * from './lib/playlist-switcher/playlist-switcher.component'; export * from './lib/recent-playlists/playlist-info/playlist-info.component'; diff --git a/libs/playlist/shared/ui/src/lib/add-playlist-menu/add-playlist-menu.component.html b/libs/playlist/shared/ui/src/lib/add-playlist-menu/add-playlist-menu.component.html deleted file mode 100644 index 124fdc7bb..000000000 --- a/libs/playlist/shared/ui/src/lib/add-playlist-menu/add-playlist-menu.component.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - diff --git a/libs/playlist/shared/ui/src/lib/add-playlist-menu/add-playlist-menu.component.ts b/libs/playlist/shared/ui/src/lib/add-playlist-menu/add-playlist-menu.component.ts deleted file mode 100644 index 7da331844..000000000 --- a/libs/playlist/shared/ui/src/lib/add-playlist-menu/add-playlist-menu.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component, output, viewChild } from '@angular/core'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatIconModule } from '@angular/material/icon'; -import { MatMenu, MatMenuModule } from '@angular/material/menu'; -import { TranslateModule } from '@ngx-translate/core'; - -export type PlaylistType = 'xtream' | 'url' | 'text' | 'file' | 'stalker'; - -@Component({ - selector: 'app-add-playlist-menu', - templateUrl: './add-playlist-menu.component.html', - imports: [MatDividerModule, MatIconModule, MatMenuModule, TranslateModule], -}) -export class AddPlaylistMenuComponent { - readonly menu = viewChild.required('addPlaylistMenu'); - - readonly playlistTypeSelected = output(); - - onPlaylistTypeClick(type: PlaylistType): void { - this.playlistTypeSelected.emit(type); - } -} diff --git a/libs/playlist/shared/ui/src/lib/add-playlist-menu/playlist-type.ts b/libs/playlist/shared/ui/src/lib/add-playlist-menu/playlist-type.ts new file mode 100644 index 000000000..2a9fa16a2 --- /dev/null +++ b/libs/playlist/shared/ui/src/lib/add-playlist-menu/playlist-type.ts @@ -0,0 +1 @@ +export type PlaylistType = 'xtream' | 'url' | 'text' | 'file' | 'stalker'; diff --git a/libs/playlist/shared/ui/src/lib/recent-playlists/empty-state/empty-state.component.html b/libs/playlist/shared/ui/src/lib/recent-playlists/empty-state/empty-state.component.html index d657e94f6..f1b95d0f4 100644 --- a/libs/playlist/shared/ui/src/lib/recent-playlists/empty-state/empty-state.component.html +++ b/libs/playlist/shared/ui/src/lib/recent-playlists/empty-state/empty-state.component.html @@ -38,14 +38,11 @@ -
diff --git a/libs/playlist/shared/ui/src/lib/recent-playlists/empty-state/empty-state.component.ts b/libs/playlist/shared/ui/src/lib/recent-playlists/empty-state/empty-state.component.ts index 72e53186f..631a683ad 100644 --- a/libs/playlist/shared/ui/src/lib/recent-playlists/empty-state/empty-state.component.ts +++ b/libs/playlist/shared/ui/src/lib/recent-playlists/empty-state/empty-state.component.ts @@ -1,9 +1,7 @@ -import { Component, input, output, viewChild } from '@angular/core'; +import { Component, input, output } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIcon } from '@angular/material/icon'; -import { MatMenuModule } from '@angular/material/menu'; import { TranslatePipe } from '@ngx-translate/core'; -import { AddPlaylistMenuComponent, PlaylistType } from '../../add-playlist-menu/add-playlist-menu.component'; export type EmptyStateType = 'welcome' | 'no-results'; @@ -11,14 +9,13 @@ export type EmptyStateType = 'welcome' | 'no-results'; selector: 'app-empty-state', templateUrl: './empty-state.component.html', styleUrls: ['./empty-state.component.scss'], - imports: [AddPlaylistMenuComponent, MatButtonModule, MatIcon, MatMenuModule, TranslatePipe], + imports: [MatButtonModule, MatIcon, TranslatePipe], }) export class EmptyStateComponent { - readonly addPlaylistMenuComponent = viewChild.required(AddPlaylistMenuComponent); readonly type = input.required(); - readonly addPlaylistClicked = output(); + readonly addPlaylistClicked = output(); - onAddPlaylist(playlistType: PlaylistType): void { - this.addPlaylistClicked.emit(playlistType); + onAddPlaylist(): void { + this.addPlaylistClicked.emit(); } } diff --git a/libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.html b/libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.html index eb0f576e1..519e7586c 100644 --- a/libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.html +++ b/libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.html @@ -4,7 +4,7 @@ } @else { diff --git a/libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.ts b/libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.ts index 9db689d7c..f4d4c074c 100644 --- a/libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.ts +++ b/libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.ts @@ -30,7 +30,7 @@ import { SortService, } from 'services'; import { PLAYLIST_UPDATE, PlaylistMeta } from 'shared-interfaces'; -import { PlaylistType } from '../add-playlist-menu/add-playlist-menu.component'; + import { EmptyStateComponent } from './empty-state/empty-state.component'; import { PlaylistInfoComponent } from './playlist-info/playlist-info.component'; import { PlaylistItemComponent } from './playlist-item/playlist-item.component'; @@ -71,7 +71,7 @@ export class RecentPlaylistsComponent { readonly sidebarMode = input(false); readonly searchQueryInput = input(''); readonly playlistClicked = output(); - readonly addPlaylistClicked = output(); + readonly addPlaylistClicked = output(); readonly allPlaylistsLoaded = this.store.selectSignal( selectPlaylistsLoadingFlag @@ -181,8 +181,8 @@ export class RecentPlaylistsComponent { ); } - onAddPlaylist(playlistType: PlaylistType) { - this.addPlaylistClicked.emit(playlistType); + onAddPlaylist() { + this.addPlaylistClicked.emit(); } getPlaylist(playlistMeta: PlaylistMeta): void { diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-header/workspace-shell-header.component.html b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-header/workspace-shell-header.component.html index dfd992a4f..2c4c742fa 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-header/workspace-shell-header.component.html +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-header/workspace-shell-header.component.html @@ -51,15 +51,12 @@ -