style(playlists): merge "add playlist" dialogs to one entrypoint dialog

Entire-Checkpoint: 84693b066142
This commit is contained in:
4gray committed 2026-03-29 15:27:46 +02:00
1 parent 3a66bf21a3
commit 69bf106999
29 files changed
+238 -219

No files matched your search

@@ -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 } : {},
});
}
);
@@ -1,20 +1,123 @@
<h2 mat-dialog-title>
{{ 'HOME.URL_UPLOAD.ADD_PLAYLIST' | translate }}: {{ playlistType }}
{{ 'HOME.URL_UPLOAD.ADD_PLAYLIST' | translate }}
</h2>
<mat-dialog-content class="mat-typography">
@if (playlistType === 'file') {
<app-file-upload
(fileRejected)="rejectFile($event)"
(fileSelected)="handlePlaylist($event)"
(closeDialog)="closeDialog()"
/>
} @else if (playlistType === 'url') {
<app-url-upload (urlAdded)="sendPlaylistsUrl($event)" />
} @else if (playlistType === 'text') {
<app-text-import (textAdded)="uploadAsText($event)" />
} @else if (playlistType === 'stalker') {
<app-stalker-portal-import (addClicked)="closeDialog()" />
} @else if (playlistType === 'xtream') {
<app-xtream-code-import (addClicked)="closeDialog()" />
<mat-button-toggle-group
[value]="category()"
(change)="category.set($event.value)"
class="category-toggle"
hideSingleSelectionIndicator
>
<mat-button-toggle value="m3u">M3U</mat-button-toggle>
<mat-button-toggle value="xtream">Xtream</mat-button-toggle>
<mat-button-toggle value="stalker">Stalker</mat-button-toggle>
</mat-button-toggle-group>
@if (category() === 'm3u') {
<mat-button-toggle-group
[value]="m3uSubType()"
(change)="m3uSubType.set($event.value)"
class="subtype-toggle"
hideSingleSelectionIndicator
>
<mat-button-toggle value="url">
{{ 'HOME.TABS.URL_UPLOAD' | translate }}
</mat-button-toggle>
<mat-button-toggle value="file">
{{ 'HOME.TABS.FILE_UPLOAD' | translate }}
</mat-button-toggle>
<mat-button-toggle value="text">
{{ 'HOME.TABS.TEXT_IMPORT' | translate }}
</mat-button-toggle>
</mat-button-toggle-group>
}
<div class="form-area">
@switch (playlistType()) {
@case ('file') {
<app-file-upload
(fileRejected)="rejectFile($event)"
(fileSelected)="handlePlaylist($event)"
(closeDialog)="closeDialog()"
/>
}
@case ('url') {
<app-url-upload (urlAdded)="sendPlaylistsUrl($event)" />
}
@case ('text') {
<app-text-import (textAdded)="uploadAsText($event)" />
}
@case ('stalker') {
<app-stalker-portal-import (addClicked)="closeDialog()" />
}
@case ('xtream') {
<app-xtream-code-import (addClicked)="closeDialog()" />
}
}
</div>
</mat-dialog-content>
<mat-dialog-actions align="end">
@switch (playlistType()) {
@case ('url') {
<button
mat-button
type="button"
[disabled]="!urlUpload()?.form?.valid"
(click)="urlUpload()?.urlAdded?.emit(urlUpload()?.form?.value?.playlistUrl)"
>
{{ 'HOME.URL_UPLOAD.ADD_PLAYLIST' | translate }}
</button>
}
@case ('text') {
<button
mat-button
type="button"
[disabled]="!textImport()?.textForm?.valid"
(click)="textImport()?.textAdded?.emit(textImport()?.textForm?.value?.text)"
>
{{ 'HOME.TEXT_IMPORT.BUTTON_LABEL' | translate }}
</button>
}
@case ('xtream') {
<button
mat-button
type="button"
[disabled]="!xtreamImport()?.form?.valid || xtreamImport()?.isTestingConnection"
(click)="xtreamImport()?.testConnection()"
>
{{ xtreamImport()?.isTestingConnection ? 'Testing...' : 'Test Connection' }}
</button>
<button
mat-button
type="button"
(click)="xtreamImport()?.clearForm()"
>
{{ 'CLEAR' | translate }}
</button>
<button
mat-button
type="button"
[disabled]="!xtreamImport()?.form?.valid"
(click)="xtreamImport()?.addPlaylist()"
>
{{ 'HOME.XTREAM_PLAYLIST.ADD' | translate }}
</button>
}
@case ('stalker') {
@if (stalkerImport()?.isLoading()) {
<button mat-button type="button" disabled>
{{ 'HOME.STALKER_PORTAL.VALIDATING' | translate }}...
</button>
} @else {
<button
mat-button
type="button"
[disabled]="!stalkerImport()?.form?.valid"
(click)="stalkerImport()?.addPlaylist()"
>
{{ 'HOME.STALKER_PORTAL.ADD' | translate }}
</button>
}
}
}
</mat-dialog-actions>
@@ -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;
}
@@ -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<PlaylistCategory>('m3u');
readonly m3uSubType = signal<M3uSubType>('url');
readonly playlistType = computed<PlaylistType>(() => {
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);
}
}
/**
@@ -118,19 +118,4 @@
}}</mat-label>
<input matInput type="text" id="password" formControlName="password" />
</mat-form-field> -->
@if (isLoading()) {
<div class="loading-container">
<mat-spinner diameter="20"></mat-spinner>
<span>{{ 'HOME.STALKER_PORTAL.VALIDATING' | translate }}</span>
</div>
} @else {
<button
mat-button
type="submit"
[disabled]="!form.valid"
(click)="addPlaylist()"
>
{{ 'HOME.STALKER_PORTAL.ADD' | translate }}
</button>
}
</form>
@@ -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,
],
@@ -1,4 +1,4 @@
<form (ngSubmit)="textAdded.emit(textForm.value.text)" [formGroup]="textForm">
<form [formGroup]="textForm">
<mat-form-field class="w-full" appearance="fill">
<mat-label>{{ 'HOME.TEXT_IMPORT.LABEL' | translate }}</mat-label>
<textarea
@@ -7,12 +7,4 @@
placeholder="#EXTM3U url-tvg=..."
></textarea>
</mat-form-field>
<button
mat-flat-button
color="primary"
type="submit"
[disabled]="!textForm.valid"
>
{{ 'HOME.TEXT_IMPORT.BUTTON_LABEL' | translate }}
</button>
</form>
@@ -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<string>();
@@ -1,4 +1,4 @@
<form (ngSubmit)="urlAdded.emit(form.value.playlistUrl)" [formGroup]="form">
<form [formGroup]="form">
<mat-form-field class="w-full">
<input
type="url"
@@ -12,13 +12,4 @@
{{ 'HOME.URL_UPLOAD.CORS_NOTE' | translate }}
</p>
}
<button
class="add-button"
mat-flat-button
[disabled]="!form.valid"
color="primary"
type="submit"
>
{{ 'HOME.URL_UPLOAD.ADD_PLAYLIST' | translate }}
</button>
</form>
@@ -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);
@@ -34,34 +34,6 @@
<input matInput type="text" id="password" formControlName="password" />
</mat-form-field>
<div class="button-row">
<div>
<button
mat-button
type="button"
[disabled]="!form.valid || isTestingConnection"
(click)="testConnection()"
>
{{ isTestingConnection ? 'Testing...' : 'Test Connection' }}
</button>
<button
mat-button
type="button"
(click)="clearForm()"
>
{{ 'CLEAR' | translate }}
</button>
</div>
<button
mat-button
type="submit"
[disabled]="!form.valid"
(click)="addPlaylist()"
>
{{ 'HOME.XTREAM_PLAYLIST.ADD' | translate }}
</button>
</div>
@if (connectionStatus) {
<div class="connection-status">
<mat-icon [class]="getStatusClass()">
@@ -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;
}
`,
],
})
+1 -1
View File
@@ -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';
@@ -1,43 +0,0 @@
<mat-menu #addPlaylistMenu="matMenu">
<button
mat-menu-item
[attr.aria-label]="'HOME.TABS.FILE_UPLOAD' | translate"
(click)="onPlaylistTypeClick('file')"
>
<mat-icon>attach_file</mat-icon>
<span>{{ 'HOME.TABS.FILE_UPLOAD' | translate }}</span>
</button>
<button
mat-menu-item
[attr.aria-label]="'HOME.TABS.URL_UPLOAD' | translate"
(click)="onPlaylistTypeClick('url')"
>
<mat-icon>link</mat-icon>
<span>{{ 'HOME.TABS.URL_UPLOAD' | translate }}</span>
</button>
<button
mat-menu-item
[attr.aria-label]="'HOME.TABS.TEXT_IMPORT' | translate"
(click)="onPlaylistTypeClick('text')"
>
<mat-icon>text_format</mat-icon>
<span>{{ 'HOME.TABS.TEXT_IMPORT' | translate }}</span>
</button>
<mat-divider></mat-divider>
<button
mat-menu-item
[attr.aria-label]="'HOME.TABS.XTREME_IMPORT' | translate"
(click)="onPlaylistTypeClick('xtream')"
>
<mat-icon>video_library</mat-icon>
<span>{{ 'HOME.TABS.XTREME_IMPORT' | translate }}</span>
</button>
<button
mat-menu-item
[attr.aria-label]="'HOME.TABS.STALKER_PORTAL_IMPORT' | translate"
(click)="onPlaylistTypeClick('stalker')"
>
<mat-icon>dashboard</mat-icon>
<span>{{ 'HOME.TABS.STALKER_PORTAL_IMPORT' | translate }}</span>
</button>
</mat-menu>
@@ -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<MatMenu>('addPlaylistMenu');
readonly playlistTypeSelected = output<PlaylistType>();
onPlaylistTypeClick(type: PlaylistType): void {
this.playlistTypeSelected.emit(type);
}
}
@@ -0,0 +1 @@
export type PlaylistType = 'xtream' | 'url' | 'text' | 'file' | 'stalker';
@@ -38,14 +38,11 @@
<button
mat-raised-button
class="primary-button"
[matMenuTriggerFor]="addPlaylistMenuComponent().menu()"
(click)="onAddPlaylist()"
>
<mat-icon>add_circle</mat-icon>
{{ 'HOME.PLAYLISTS.ADD_YOUR_FIRST' | translate }}
</button>
<app-add-playlist-menu
(playlistTypeSelected)="onAddPlaylist($event)"
/>
</div>
<div class="features">
@@ -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<EmptyStateType>();
readonly addPlaylistClicked = output<PlaylistType>();
readonly addPlaylistClicked = output<void>();
onAddPlaylist(playlistType: PlaylistType): void {
this.addPlaylistClicked.emit(playlistType);
onAddPlaylist(): void {
this.addPlaylistClicked.emit();
}
}
@@ -4,7 +4,7 @@
<!-- Welcome state: no playlists at all -->
<app-empty-state
[type]="'welcome'"
(addPlaylistClicked)="onAddPlaylist($event)"
(addPlaylistClicked)="onAddPlaylist()"
/>
} @else {
<!-- No results state: search/filter returned nothing -->
@@ -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<string>('');
readonly playlistClicked = output<string>();
readonly addPlaylistClicked = output<PlaylistType>();
readonly addPlaylistClicked = output<void>();
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 {
@@ -51,15 +51,12 @@
<button
type="button"
mat-icon-button
[matMenuTriggerFor]="addPlaylistMenu().menu()"
(click)="onAddPlaylistRequested()"
[matTooltip]="'WORKSPACE.SHELL.ADD_PLAYLIST_ALL' | translate"
[attr.aria-label]="'WORKSPACE.SHELL.ADD_PLAYLIST' | translate"
>
<mat-icon>add</mat-icon>
</button>
<app-add-playlist-menu
(playlistTypeSelected)="onAddPlaylistRequested($event)"
/>
<span class="header-actions-divider" aria-hidden="true"></span>
<button
type="button"
@@ -7,12 +7,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatIconButton } from '@angular/material/button';
import { MatIcon } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatTooltip } from '@angular/material/tooltip';
import { TranslateService } from '@ngx-translate/core';
import { of } from 'rxjs';
import { TranslatePipe } from '@ngx-translate/core';
import { AddPlaylistMenuComponent } from '@iptvnator/playlist/shared/ui';
import { WorkspaceShellHeaderComponent } from './workspace-shell-header.component';
@Component({
@@ -55,10 +53,8 @@ describe('WorkspaceShellHeaderComponent', () => {
.overrideComponent(WorkspaceShellHeaderComponent, {
set: {
imports: [
AddPlaylistMenuComponent,
MatIcon,
MatIconButton,
MatMenuModule,
MatTooltip,
MockPlaylistSwitcherComponent,
TranslatePipe,
@@ -3,27 +3,19 @@ import {
Component,
input,
output,
viewChild,
} from '@angular/core';
import { MatIconButton } from '@angular/material/button';
import { MatIcon } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatTooltip } from '@angular/material/tooltip';
import { TranslatePipe } from '@ngx-translate/core';
import {
AddPlaylistMenuComponent,
PlaylistSwitcherComponent,
PlaylistType,
} from '@iptvnator/playlist/shared/ui';
import { PlaylistSwitcherComponent } from '@iptvnator/playlist/shared/ui';
import { WorkspaceHeaderAction } from '@iptvnator/portal/shared/util';
@Component({
selector: 'app-workspace-shell-header',
imports: [
AddPlaylistMenuComponent,
MatIcon,
MatIconButton,
MatMenuModule,
MatTooltip,
PlaylistSwitcherComponent,
TranslatePipe,
@@ -33,8 +25,6 @@ import { WorkspaceHeaderAction } from '@iptvnator/portal/shared/util';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class WorkspaceShellHeaderComponent {
readonly addPlaylistMenu = viewChild.required(AddPlaylistMenuComponent);
readonly playlistTitle = input('');
readonly playlistSubtitle = input('');
readonly canOpenPlaylistInfo = input(false);
@@ -52,7 +42,7 @@ export class WorkspaceShellHeaderComponent {
readonly searchChanged = output<string>();
readonly searchSubmitted = output<string>();
readonly commandPaletteRequested = output<void>();
readonly addPlaylistRequested = output<PlaylistType>();
readonly addPlaylistRequested = output<void>();
readonly globalFavoritesRequested = output<void>();
readonly headerShortcutRequested = output<void>();
readonly downloadsRequested = output<void>();
@@ -77,8 +67,8 @@ export class WorkspaceShellHeaderComponent {
this.accountInfoRequested.emit();
}
onAddPlaylistRequested(type: PlaylistType): void {
this.addPlaylistRequested.emit(type);
onAddPlaylistRequested(): void {
this.addPlaylistRequested.emit();
}
onCommandPaletteRequested(): void {
@@ -12,10 +12,7 @@ import { NavigationEnd, Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
import { filter, firstValueFrom, startWith } from 'rxjs';
import {
PlaylistInfoComponent,
PlaylistType,
} from '@iptvnator/playlist/shared/ui';
import { PlaylistInfoComponent } from '@iptvnator/playlist/shared/ui';
import { PlaylistContextFacade } from '@iptvnator/playlist/shared/util';
import {
buildPortalRailLinks,
@@ -669,8 +666,8 @@ export class WorkspaceShellFacade {
this.applySearchQuery(trimmedValue);
}
openAddPlaylistDialog(type: PlaylistType): void {
this.workspaceActions.openAddPlaylistDialog(type);
openAddPlaylistDialog(): void {
this.workspaceActions.openAddPlaylistDialog();
}
openCommandPalette(): void {
@@ -73,7 +73,7 @@
(searchChanged)="facade.onSearchInput($event)"
(searchSubmitted)="facade.onSearchEnter($event)"
(commandPaletteRequested)="facade.openCommandPalette()"
(addPlaylistRequested)="facade.openAddPlaylistDialog($event)"
(addPlaylistRequested)="facade.openAddPlaylistDialog()"
(globalFavoritesRequested)="facade.navigateToGlobalFavorites()"
(headerShortcutRequested)="facade.runHeaderShortcut()"
(downloadsRequested)="facade.openDownloadsShortcut()"
@@ -54,7 +54,7 @@ class MockWorkspaceShellHeaderComponent {
readonly searchChanged = output<string>();
readonly searchSubmitted = output<string>();
readonly commandPaletteRequested = output<void>();
readonly addPlaylistRequested = output<string>();
readonly addPlaylistRequested = output<void>();
readonly globalFavoritesRequested = output<void>();
readonly headerShortcutRequested = output<void>();
readonly downloadsRequested = output<void>();
@@ -6,7 +6,7 @@
<div class="sources-content">
<app-recent-playlists
[searchQueryInput]="searchQuery()"
(addPlaylistClicked)="onAddPlaylist($event)"
(addPlaylistClicked)="onAddPlaylist()"
/>
</div>
</section>
@@ -2,10 +2,7 @@ import { Component, computed, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';
import {
PlaylistType,
RecentPlaylistsComponent,
} from '@iptvnator/playlist/shared/ui';
import { RecentPlaylistsComponent } from '@iptvnator/playlist/shared/ui';
import { TranslateService } from '@ngx-translate/core';
import { selectActiveTypeFilters, selectAllPlaylistsMeta } from 'm3u-state';
import { map, startWith } from 'rxjs';
@@ -100,8 +97,8 @@ export class WorkspaceSourcesComponent {
});
});
onAddPlaylist(playlistType: PlaylistType): void {
this.workspaceActions.openAddPlaylistDialog(playlistType);
onAddPlaylist(): void {
this.workspaceActions.openAddPlaylistDialog();
}
private translateText(
@@ -1,6 +1,8 @@
import { InjectionToken } from '@angular/core';
export type WorkspacePlaylistType = 'xtream' | 'url' | 'text' | 'file' | 'stalker';
export type PlaylistCategory = 'm3u' | 'xtream' | 'stalker';
export type M3uSubType = 'url' | 'file' | 'text';
export interface WorkspaceAccountInfoData {
vodStreamsCount: number;
@@ -9,7 +11,7 @@ export interface WorkspaceAccountInfoData {
}
export interface WorkspaceShellActions {
openAddPlaylistDialog(type: WorkspacePlaylistType): void;
openAddPlaylistDialog(type?: WorkspacePlaylistType): void;
openGlobalSearch(initialQuery?: string): void;
openGlobalRecent(): void;
openAccountInfo(data: WorkspaceAccountInfoData): void;