From 9834c9fc0dcba671e7aba16f3d1c2581ca4d9146 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sun, 8 Mar 2026 21:49:39 +0100 Subject: [PATCH] refactor(portals): more structural changes in monorepo for xtream and stalker feat(xtream): implement category management dialog and main container with sorting and searching features style(stalker, xtream): add styles for main container, shell, and category management dialog fix(xtream): update routing and state management for playlist and content sections chore: update tsconfig paths for stalker and xtream features --- apps/web/src/app/app.config.ts | 2 + .../portal-shared/portal-main-container.scss | 80 +------------------ .../portal-navigation-actions.service.ts | 59 ++++++++++++++ .../app/stalker/stalker-feature-loaders.ts | 8 -- .../workspace-context-panel.component.ts | 2 +- .../live-stream-layout.component.ts | 2 +- .../xtream-electron/xtream-feature-loaders.ts | 8 -- libs/portal/shared/ui/src/index.ts | 1 + .../lib}/navigation/navigation.component.html | 7 +- .../lib}/navigation/navigation.component.scss | 0 .../lib}/navigation/navigation.component.ts | 59 +++++--------- .../src/lib/styles/portal-main-container.scss | 78 ++++++++++++++++++ libs/portal/shared/util/src/index.ts | 1 + .../util/src/lib/portal-navigation-actions.ts | 11 +++ libs/portal/stalker/feature/src/index.ts | 2 + .../feature/src/lib/stalker-feature.routes.ts | 18 +++-- .../stalker-main-container.component.html | 0 .../stalker-main-container.component.scss | 0 .../lib}/stalker-main-container.component.ts | 19 ++--- .../stalker-shell.component.html | 2 +- .../stalker-shell.component.scss | 0 .../stalker-shell/stalker-shell.component.ts | 41 +++++----- libs/portal/stalker/feature/tsconfig.json | 2 +- libs/portal/xtream/feature/src/index.ts | 4 + .../category-management-dialog.component.html | 0 .../category-management-dialog.component.scss | 0 .../category-management-dialog.component.ts | 14 +--- .../src/lib}/loading-overlay.component.ts | 0 .../feature/src/lib/xtream-feature.routes.ts | 18 +++-- .../lib}/xtream-main-container.component.html | 1 - .../lib}/xtream-main-container.component.ts | 41 ++++++---- .../xtream-shell/xtream-shell.component.html | 4 +- .../xtream-shell/xtream-shell.component.scss | 3 +- .../xtream-shell/xtream-shell.component.ts | 36 ++------- libs/portal/xtream/feature/tsconfig.json | 2 +- 35 files changed, 274 insertions(+), 251 deletions(-) create mode 100644 apps/web/src/app/services/portal-navigation-actions.service.ts rename {apps/web/src/app/portal-shared => libs/portal/shared/ui/src/lib}/navigation/navigation.component.html (86%) rename {apps/web/src/app/portal-shared => libs/portal/shared/ui/src/lib}/navigation/navigation.component.scss (100%) rename {apps/web/src/app/portal-shared => libs/portal/shared/ui/src/lib}/navigation/navigation.component.ts (60%) create mode 100644 libs/portal/shared/ui/src/lib/styles/portal-main-container.scss create mode 100644 libs/portal/shared/util/src/lib/portal-navigation-actions.ts rename {apps/web/src/app/stalker => libs/portal/stalker/feature/src/lib}/stalker-main-container.component.html (100%) rename {apps/web/src/app/stalker => libs/portal/stalker/feature/src/lib}/stalker-main-container.component.scss (100%) rename {apps/web/src/app/stalker => libs/portal/stalker/feature/src/lib}/stalker-main-container.component.ts (87%) rename {apps/web/src/app/stalker => libs/portal/stalker/feature/src/lib}/stalker-shell/stalker-shell.component.html (70%) rename {apps/web/src/app/stalker => libs/portal/stalker/feature/src/lib}/stalker-shell/stalker-shell.component.scss (100%) rename {apps/web/src/app/stalker => libs/portal/stalker/feature/src/lib}/stalker-shell/stalker-shell.component.ts (70%) rename {apps/web/src/app/xtream-electron => libs/portal/xtream/feature/src/lib}/category-management-dialog/category-management-dialog.component.html (100%) rename {apps/web/src/app/xtream-electron => libs/portal/xtream/feature/src/lib}/category-management-dialog/category-management-dialog.component.scss (100%) rename {apps/web/src/app/xtream-electron => libs/portal/xtream/feature/src/lib}/category-management-dialog/category-management-dialog.component.ts (91%) rename {apps/web/src/app/xtream-electron/loading-overlay => libs/portal/xtream/feature/src/lib}/loading-overlay.component.ts (100%) rename {apps/web/src/app/xtream-electron => libs/portal/xtream/feature/src/lib}/xtream-main-container.component.html (99%) rename {apps/web/src/app/xtream-electron => libs/portal/xtream/feature/src/lib}/xtream-main-container.component.ts (85%) rename {apps/web/src/app/xtream-electron => libs/portal/xtream/feature/src/lib}/xtream-shell/xtream-shell.component.html (71%) rename {apps/web/src/app/xtream-electron => libs/portal/xtream/feature/src/lib}/xtream-shell/xtream-shell.component.scss (82%) rename {apps/web/src/app/xtream-electron => libs/portal/xtream/feature/src/lib}/xtream-shell/xtream-shell.component.ts (71%) diff --git a/apps/web/src/app/app.config.ts b/apps/web/src/app/app.config.ts index 1ae8aff9c..76e8c3fe7 100644 --- a/apps/web/src/app/app.config.ts +++ b/apps/web/src/app/app.config.ts @@ -28,6 +28,7 @@ import { AppConfig } from '../environments/environment'; import { routes } from './app.routes'; import { ElectronService } from './services/electron.service'; import { PlayerService } from './services/player.service'; +import { providePortalNavigationActions } from './services/portal-navigation-actions.service'; import { PwaService } from './services/pwa.service'; // AoT requires an exported function for factories @@ -91,6 +92,7 @@ export const appConfig: ApplicationConfig = { provide: PORTAL_PLAYER, useExisting: PlayerService, }, + ...providePortalNavigationActions(), ...provideXtreamDataSource(), ], }; diff --git a/apps/web/src/app/portal-shared/portal-main-container.scss b/apps/web/src/app/portal-shared/portal-main-container.scss index 810251856..0a582a570 100644 --- a/apps/web/src/app/portal-shared/portal-main-container.scss +++ b/apps/web/src/app/portal-shared/portal-main-container.scss @@ -1,79 +1 @@ -@use '@angular/material' as mat; - -:host { - display: flex; - flex-direction: row; - overflow: hidden; - height: 100%; - width: 100%; -} - -.sidebar-header { - display: flex; - flex-direction: row; - align-items: center; - app-region: drag; - - &.sidebar-header--workspace { - justify-content: flex-end; - } - - button { - @include mat.form-field-density(-5); - app-region: no-drag; - } -} - -.main-content { - flex: 1; - display: flex; - flex-direction: column; - min-height: 0; // Important for proper flex behavior -} - -.content-header { - padding: 16px; - border-bottom: 1px solid rgba(0, 0, 0, 0.12); - display: flex; - align-items: center; - justify-content: space-between; - app-region: drag; - - h2 { - margin: 0; - font-size: 1.2rem; - font-weight: 500; - display: flex; - align-items: center; - gap: 10px; - } - - button { - app-region: no-drag; - } -} - -.header-left { - min-width: 0; -} - -.header-actions { - display: flex; - align-items: center; - gap: 8px; - app-region: no-drag; -} - -.category-search { - width: 240px; -} - -.scrollable-content { - flex: 1; - overflow-y: auto; - - &.no-header { - // When header is hidden, content starts from top - margin-top: 0; - } -} +@forward '../../../../../libs/portal/shared/ui/src/lib/styles/portal-main-container'; diff --git a/apps/web/src/app/services/portal-navigation-actions.service.ts b/apps/web/src/app/services/portal-navigation-actions.service.ts new file mode 100644 index 000000000..55e906776 --- /dev/null +++ b/apps/web/src/app/services/portal-navigation-actions.service.ts @@ -0,0 +1,59 @@ +import { inject, Injectable, Provider } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { PlaylistInfoComponent } from 'components'; +import { + PORTAL_NAVIGATION_ACTIONS, + PortalNavigationActions, +} from '@iptvnator/portal/shared/util'; +import { XtreamStore } from '@iptvnator/portal/xtream/data-access'; +import { Playlist } from 'shared-interfaces'; +import { SettingsComponent } from '../settings/settings.component'; +import { AccountInfoComponent } from '../xtream-electron/account-info/account-info.component'; + +@Injectable({ providedIn: 'root' }) +export class AppPortalNavigationActionsService + implements PortalNavigationActions +{ + private readonly dialog = inject(MatDialog); + private readonly xtreamStore = inject(XtreamStore); + + openAccountInfo(): void { + this.dialog.open(AccountInfoComponent, { + width: '80%', + maxWidth: '1200px', + maxHeight: '90vh', + data: { + vodStreamsCount: this.xtreamStore.vodStreams().length, + liveStreamsCount: this.xtreamStore.liveStreams().length, + seriesCount: this.xtreamStore.serialStreams().length, + }, + }); + } + + openPlaylistInfo(playlist: Playlist | null | undefined): void { + this.dialog.open(PlaylistInfoComponent, { + data: playlist ?? null, + }); + } + + openSettings(): void { + this.dialog.open(SettingsComponent, { + width: '1200px', + maxWidth: '96vw', + maxHeight: '92vh', + data: { + isDialog: true, + }, + }); + } +} + +export function providePortalNavigationActions(): Provider[] { + return [ + AppPortalNavigationActionsService, + { + provide: PORTAL_NAVIGATION_ACTIONS, + useExisting: AppPortalNavigationActionsService, + }, + ]; +} diff --git a/apps/web/src/app/stalker/stalker-feature-loaders.ts b/apps/web/src/app/stalker/stalker-feature-loaders.ts index 227c37834..fbb8570bc 100644 --- a/apps/web/src/app/stalker/stalker-feature-loaders.ts +++ b/apps/web/src/app/stalker/stalker-feature-loaders.ts @@ -4,14 +4,6 @@ import { provideStalkerCatalogFacade } from './stalker-catalog-facade.service'; // Temporary app-side loader map while Stalker route screens remain in apps/web. export const stalkerFeatureRouteOptions: StalkerFeatureRouteOptions = { catalogProviders: provideStalkerCatalogFacade(), - loadShellComponent: () => - import('./stalker-shell/stalker-shell.component').then( - (c) => c.StalkerShellComponent - ), - loadMainContainerComponent: () => - import('./stalker-main-container.component').then( - (c) => c.StalkerMainContainerComponent - ), loadCategoryContentViewComponent: () => import( '../portal-shared/category-content-view/category-content-view.component' diff --git a/apps/web/src/app/workspace/workspace-context-panel.component.ts b/apps/web/src/app/workspace/workspace-context-panel.component.ts index b8657fab8..041c0890b 100644 --- a/apps/web/src/app/workspace/workspace-context-panel.component.ts +++ b/apps/web/src/app/workspace/workspace-context-panel.component.ts @@ -11,7 +11,7 @@ import { StalkerStore } from '@iptvnator/portal/stalker/data-access'; import { CategoryManagementDialogComponent, CategoryManagementDialogData, -} from '../xtream-electron/category-management-dialog/category-management-dialog.component'; +} from '@iptvnator/portal/xtream/feature'; import { XtreamStore } from '@iptvnator/portal/xtream/data-access'; type WorkspaceProvider = 'xtreams' | 'stalker' | 'playlists'; diff --git a/apps/web/src/app/xtream-electron/live-stream-layout/live-stream-layout.component.ts b/apps/web/src/app/xtream-electron/live-stream-layout/live-stream-layout.component.ts index 2a1e2ab52..ebcf7e1bf 100644 --- a/apps/web/src/app/xtream-electron/live-stream-layout/live-stream-layout.component.ts +++ b/apps/web/src/app/xtream-electron/live-stream-layout/live-stream-layout.component.ts @@ -37,7 +37,7 @@ import { import { CategoryManagementDialogComponent, CategoryManagementDialogData, -} from '../category-management-dialog/category-management-dialog.component'; +} from '@iptvnator/portal/xtream/feature'; import { PortalChannelsListComponent } from '../portal-channels-list/portal-channels-list.component'; import { FavoritesService } from '@iptvnator/portal/xtream/data-access'; import { XtreamStore } from '@iptvnator/portal/xtream/data-access'; diff --git a/apps/web/src/app/xtream-electron/xtream-feature-loaders.ts b/apps/web/src/app/xtream-electron/xtream-feature-loaders.ts index c2cfb28aa..45b21ed63 100644 --- a/apps/web/src/app/xtream-electron/xtream-feature-loaders.ts +++ b/apps/web/src/app/xtream-electron/xtream-feature-loaders.ts @@ -5,18 +5,10 @@ import { provideXtreamCatalogFacade } from './xtream-catalog-facade.service'; // out of apps/web into the Xtream feature library. export const xtreamFeatureRouteOptions: XtreamFeatureRouteOptions = { catalogProviders: provideXtreamCatalogFacade(), - loadShellComponent: () => - import('./xtream-shell/xtream-shell.component').then( - (c) => c.XtreamShellComponent - ), loadLiveStreamLayoutComponent: () => import('./live-stream-layout/live-stream-layout.component').then( (c) => c.LiveStreamLayoutComponent ), - loadMainContainerComponent: () => - import('./xtream-main-container.component').then( - (c) => c.XtreamMainContainerComponent - ), loadCategoryContentViewComponent: () => import( '../portal-shared/category-content-view/category-content-view.component' diff --git a/libs/portal/shared/ui/src/index.ts b/libs/portal/shared/ui/src/index.ts index a35316de2..0fcf87851 100644 --- a/libs/portal/shared/ui/src/index.ts +++ b/libs/portal/shared/ui/src/index.ts @@ -9,4 +9,5 @@ export * from './lib/components/portal-empty-state/portal-empty-state.component' export * from './lib/components/search-form/search-form.component'; export * from './lib/components/search-layout/search-layout.component'; export * from './lib/components/watched-badge/watched-badge.component'; +export * from './lib/navigation/navigation.component'; export * from './lib/navigation/portal-rail-links.component'; diff --git a/apps/web/src/app/portal-shared/navigation/navigation.component.html b/libs/portal/shared/ui/src/lib/navigation/navigation.component.html similarity index 86% rename from apps/web/src/app/portal-shared/navigation/navigation.component.html rename to libs/portal/shared/ui/src/lib/navigation/navigation.component.html index e96594ca3..4492702d4 100644 --- a/apps/web/src/app/portal-shared/navigation/navigation.component.html +++ b/libs/portal/shared/ui/src/lib/navigation/navigation.component.html @@ -1,13 +1,12 @@
{{ getStatusIcon() }}