standalone

This commit is contained in:
tidusjar 2025-09-13 23:20:24 +01:00
parent c72a37557b
commit eacaee4476
18 changed files with 285 additions and 72 deletions

View File

@ -123,6 +123,47 @@ Modern Angular applications should use the new control flow syntax (`@if`, `@for
## Phase 3: Feature Modules Migration 🔄 IN PROGRESS
### DiscoverModule ✅ COMPLETED
**Status**: Successfully converted all components to standalone
**Components Converted**: 8 components
- DiscoverComponent ✅
- DiscoverCardComponent ✅
- DiscoverCollectionsComponent ✅
- DiscoverActorComponent ✅
- DiscoverSearchResultsComponent ✅
- CarouselListComponent ✅
- RecentlyRequestedListComponent ✅
- GenreButtonSelectComponent ✅
**Technical Changes**:
- All components converted to `standalone: true`
- Added proper imports (CommonModule, TranslateModule, Angular Material, PrimeNG)
- Updated DiscoverModule to import standalone components instead of declaring them
- Maintained all existing functionality and modern Angular patterns (signals, computed, inject)
**Build Results**:
- **Status**: ✅ Successful
- **Build Time**: ~5.6 seconds
- **Bundle Size**: ~11.66MB (no change)
- **Lazy Chunk Size**: 210.73 kB (DiscoverModule)
### VoteModule ✅ COMPLETED
**Status**: Successfully converted to standalone
**Components Converted**: 1 component
- VoteComponent ✅
**Technical Changes**:
- VoteComponent converted to `standalone: true`
- Added proper imports (CommonModule, TranslateModule, PrimeNG modules)
- Updated VoteModule to import standalone component instead of declaring it
- Maintained all existing functionality
**Build Results**:
- **Status**: ✅ Successful
- **Build Time**: ~4.9 seconds (improved)
- **Bundle Size**: ~11.66MB (no change)
- **Lazy Chunk Size**: 93.23 kB (VoteModule)
### Status: 🔄 IN PROGRESS
**Target Components**: 16 feature modules
**Estimated Duration**: 2-3 weeks

View File

@ -10,9 +10,11 @@
<div class="text-right year"><sup>{{request.releaseDate | date:'yyyy'}}</sup></div>
<h3 id="detailed-request-title-{{request.mediaId}}">{{request.title}}</h3>
</div>
<div class="col-12" *ngIf="request.username">
<p id="detailed-request-requestedby-{{request.mediaId}}">{{'MediaDetails.RequestedBy' | translate}} {{request.username}}</p>
</div>
@if (request.username) {
<div class="col-12">
<p id="detailed-request-requestedby-{{request.mediaId}}">{{'MediaDetails.RequestedBy' | translate}} {{request.username}}</p>
</div>
}
<div class="col-12">
<p id="detailed-request-date-{{request.mediaId}}">{{'MediaDetails.OnDate' | translate}} {{request.requestDate | ombiDate: 'Ppp'}}</p>
</div>
@ -21,25 +23,29 @@
</div>
</div>
<div class="row action-items">
<div class="col-12" *ngIf="isAdmin">
<div *ngIf="!request.approved && !request.denied">
<button
id="detailed-request-approve-{{request.mediaId}}"
color="accent"
mat-raised-button
(click)="approve()"
matTooltip="{{'Common.Approve' | translate}}">
<mat-icon>check-circle</mat-icon>
</button>
<button
id="detailed-request-deny-{{request.mediaId}}"
color="accent"
mat-raised-button
(click)="deny()"
matTooltip="{{'Requests.Deny' | translate}}">
<mat-icon>cancel</mat-icon></button>
@if (isAdmin) {
<div class="col-12">
@if (!request.approved && !request.denied) {
<div>
<button
id="detailed-request-approve-{{request.mediaId}}"
color="accent"
mat-raised-button
(click)="approve()"
matTooltip="{{'Common.Approve' | translate}}">
<mat-icon>check-circle</mat-icon>
</button>
<button
id="detailed-request-deny-{{request.mediaId}}"
color="accent"
mat-raised-button
(click)="deny()"
matTooltip="{{'Requests.Deny' | translate}}">
<mat-icon>cancel</mat-icon></button>
</div>
}
</div>
</div>
}
</div>
</div>
</div>

View File

@ -6,16 +6,32 @@ import {
OnInit,
Output,
} from "@angular/core";
import { CommonModule } from "@angular/common";
import { MatButtonModule } from "@angular/material/button";
import { MatIconModule } from "@angular/material/icon";
import { MatTooltipModule } from "@angular/material/tooltip";
import { TranslateModule } from "@ngx-translate/core";
import { IRecentlyRequested, RequestType } from "../../interfaces";
import { ImageService } from "app/services";
import { Subject, takeUntil } from "rxjs";
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
import { ImageComponent } from "../image/image.component";
import { OmbiDatePipe } from "../../pipes/OmbiDatePipe";
@Component({
standalone: false,
standalone: true,
selector: "ombi-detailed-card",
templateUrl: "./detailed-card.component.html",
styleUrls: ["./detailed-card.component.scss"],
imports: [
CommonModule,
MatButtonModule,
MatIconModule,
MatTooltipModule,
TranslateModule,
ImageComponent,
OmbiDatePipe
]
})
export class DetailedCardComponent implements OnInit, OnDestroy {
@Input() public request: IRecentlyRequested;

View File

@ -1,4 +1,9 @@
import { Component, OnInit } from "@angular/core";
import { CommonModule } from "@angular/common";
import { TranslateModule } from "@ngx-translate/core";
import { CarouselModule } from 'primeng/carousel';
import { SkeletonModule } from 'primeng/skeleton';
import { ActivatedRoute } from "@angular/router";
import { SearchV2Service } from "../../../services";
import { IActorCredits, IActorCast, IActorCrew } from "../../../interfaces/ISearchTvResultV2";
@ -7,11 +12,19 @@ import { RequestType } from "../../../interfaces";
import { AuthService } from "../../../auth/auth.service";
import { forkJoin } from "rxjs";
import { FeaturesFacade } from "../../../state/features/features.facade";
import { DiscoverCardComponent } from "../card/discover-card.component";
@Component({
standalone: false,
standalone: true,
templateUrl: "./discover-actor.component.html",
styleUrls: ["./discover-actor.component.scss"],
imports: [
CommonModule,
TranslateModule,
CarouselModule,
SkeletonModule,
DiscoverCardComponent
]
})
export class DiscoverActorComponent implements OnInit {
public actorId: number;

View File

@ -1,6 +1,12 @@
import { Component, Input, OnInit } from "@angular/core";
import { MessageService, RequestService, SearchV2Service } from "../../../services";
import { CommonModule } from "@angular/common";
import { RouterModule } from "@angular/router";
import { MatButtonModule } from "@angular/material/button";
import { MatMenuModule } from "@angular/material/menu";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { TranslateModule } from "@ngx-translate/core";
import { MessageService, RequestService, SearchV2Service } from "../../../services";
import { AdminRequestDialogComponent } from "../../../shared/admin-request-dialog/admin-request-dialog.component";
import { DiscoverType } from "../carousel-list/carousel-list.component";
import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component";
@ -10,12 +16,22 @@ import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2";
import { MatDialog } from "@angular/material/dialog";
import { IMovieRequestModel, RequestType } from "../../../interfaces";
import { TranslateService } from "@ngx-translate/core";
import { ImageComponent } from "../../../components";
@Component({
standalone: false,
standalone: true,
selector: "discover-card",
templateUrl: "./discover-card.component.html",
styleUrls: ["./discover-card.component.scss"],
imports: [
CommonModule,
RouterModule,
MatButtonModule,
MatMenuModule,
MatProgressSpinnerModule,
TranslateModule,
ImageComponent
]
})
export class DiscoverCardComponent implements OnInit {

View File

@ -1,12 +1,17 @@
import { Component, ViewChild, Inject, input, output, signal, computed, inject, ChangeDetectionStrategy } from "@angular/core";
import { CommonModule } from "@angular/common";
import { MatButtonToggleModule, MatButtonToggleChange } from '@angular/material/button-toggle';
import { TranslateModule } from "@ngx-translate/core";
import { CarouselModule, Carousel } from 'primeng/carousel';
import { SkeletonModule } from 'primeng/skeleton';
import { DiscoverOption, IDiscoverCardResult } from "../../interfaces";
import { ISearchMovieResult, ISearchTvResult, RequestType } from "../../../interfaces";
import { SearchV2Service } from "../../../services";
import { StorageService } from "../../../shared/storage/storage-service";
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { Carousel } from 'primeng/carousel';
import { FeaturesFacade } from "../../../state/features/features.facade";
import { APP_BASE_HREF } from "@angular/common";
import { DiscoverCardComponent } from "../card/discover-card.component";
export enum DiscoverType {
Upcoming,
@ -17,11 +22,19 @@ export enum DiscoverType {
}
@Component({
standalone: false,
standalone: true,
selector: "carousel-list",
templateUrl: "./carousel-list.component.html",
styleUrls: ["./carousel-list.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CommonModule,
MatButtonToggleModule,
TranslateModule,
CarouselModule,
SkeletonModule,
DiscoverCardComponent
]
})
export class CarouselListComponent {
// Inputs using new input() function

View File

@ -1,6 +1,10 @@
import { Component, OnInit } from "@angular/core";
import { MessageService, SearchV2Service } from "../../../services";
import { CommonModule } from "@angular/common";
import { TranslateModule } from "@ngx-translate/core";
import { CarouselModule } from 'primeng/carousel';
import { SkeletonModule } from 'primeng/skeleton';
import { MessageService, SearchV2Service } from "../../../services";
import { TranslateService } from "@ngx-translate/core";
import { ActivatedRoute } from "@angular/router";
import { AuthService } from "../../../auth/auth.service";
@ -9,11 +13,19 @@ import { IMovieCollectionsViewModel } from "../../../interfaces/ISearchTvResultV
import { RequestServiceV2 } from "../../../services/requestV2.service";
import { RequestType } from "../../../interfaces";
import { FeaturesFacade } from "../../../state/features/features.facade";
import { DiscoverCardComponent } from "../card/discover-card.component";
@Component({
standalone: false,
standalone: true,
templateUrl: "./discover-collections.component.html",
styleUrls: ["./discover-collections.component.scss"],
imports: [
CommonModule,
TranslateModule,
CarouselModule,
SkeletonModule,
DiscoverCardComponent
]
})
export class DiscoverCollectionsComponent implements OnInit {

View File

@ -1,13 +1,27 @@
import { Component, computed, inject, signal, ChangeDetectionStrategy } from "@angular/core";
import { CommonModule } from "@angular/common";
import { TranslateModule } from "@ngx-translate/core";
import { SkeletonModule } from "primeng/skeleton";
import { AuthService } from "../../../auth/auth.service";
import { DiscoverType } from "../carousel-list/carousel-list.component";
import { GenreButtonSelectComponent } from "../genre/genre-button-select.component";
import { RecentlyRequestedListComponent } from "../recently-requested-list/recently-requested-list.component";
import { CarouselListComponent } from "../carousel-list/carousel-list.component";
@Component({
standalone: false,
standalone: true,
templateUrl: "./discover.component.html",
styleUrls: ["./discover.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CommonModule,
TranslateModule,
SkeletonModule,
GenreButtonSelectComponent,
RecentlyRequestedListComponent,
CarouselListComponent
]
})
export class DiscoverComponent {
// Services using inject() function

View File

@ -1,6 +1,10 @@
import { Component, OnInit, computed, signal } from "@angular/core";
import { CommonModule } from "@angular/common";
import { MatButtonToggleModule, MatButtonToggleChange } from "@angular/material/button-toggle";
import { TranslateModule } from "@ngx-translate/core";
import { SkeletonModule } from "primeng/skeleton";
import { SearchV2Service } from "../../../services";
import { MatButtonToggleChange } from "@angular/material/button-toggle";
import { RequestType } from "../../../interfaces";
import { AdvancedSearchDialogDataService } from "app/shared/advanced-search-dialog/advanced-search-dialog-data.service";
import { Router } from "@angular/router";
@ -13,10 +17,16 @@ interface IGenreSelect {
type: "movie"|"tv";
}
@Component({
standalone: false,
standalone: true,
selector: "genre-button-select",
templateUrl: "./genre-button-select.component.html",
styleUrls: ["./genre-button-select.component.scss"],
imports: [
CommonModule,
MatButtonToggleModule,
TranslateModule,
SkeletonModule
]
})
export class GenreButtonSelectComponent implements OnInit {

View File

@ -1,6 +1,14 @@
import { Component, OnInit, Input, ViewChild, OnDestroy, signal } from "@angular/core";
import { CommonModule } from "@angular/common";
import { RouterModule } from "@angular/router";
import { MatButtonModule } from "@angular/material/button";
import { MatMenuModule } from "@angular/material/menu";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { TranslateModule } from "@ngx-translate/core";
import { CarouselModule, Carousel } from 'primeng/carousel';
import { SkeletonModule } from 'primeng/skeleton';
import { IRecentlyRequested, IRequestEngineResult, RequestType } from "../../../interfaces";
import { Carousel } from 'primeng/carousel';
import { ResponsiveOptions } from "../carousel.options";
import { RequestServiceV2 } from "app/services/requestV2.service";
import { finalize, map, Observable, Subject, takeUntil, tap } from "rxjs";
@ -10,6 +18,8 @@ import { NotificationService, RequestService } from "app/services";
import { TranslateService } from "@ngx-translate/core";
import { DenyDialogComponent } from '../../../media-details/components/shared/deny-dialog/deny-dialog.component';
import { MatDialog } from "@angular/material/dialog";
import { ImageComponent } from "../../../components";
import { DetailedCardComponent } from "../../../components";
export enum DiscoverType {
Upcoming,
@ -20,10 +30,22 @@ export enum DiscoverType {
}
@Component({
standalone: false,
standalone: true,
selector: "ombi-recently-list",
templateUrl: "./recently-requested-list.component.html",
styleUrls: ["./recently-requested-list.component.scss"],
imports: [
CommonModule,
RouterModule,
MatButtonModule,
MatMenuModule,
MatProgressSpinnerModule,
TranslateModule,
CarouselModule,
SkeletonModule,
ImageComponent,
DetailedCardComponent
]
})
export class RecentlyRequestedListComponent implements OnInit, OnDestroy {

View File

@ -1,7 +1,11 @@
import { ActivatedRoute, NavigationEnd, Router } from "@angular/router";
import { Component, OnInit } from "@angular/core";
import { IMultiSearchResult, ISearchMovieResult, RequestType } from "../../../interfaces";
import { CommonModule } from "@angular/common";
import { TranslateModule } from "@ngx-translate/core";
import { CarouselModule } from 'primeng/carousel';
import { SkeletonModule } from 'primeng/skeleton';
import { IMultiSearchResult, ISearchMovieResult, RequestType } from "../../../interfaces";
import { AdvancedSearchDialogDataService } from "../../../shared/advanced-search-dialog/advanced-search-dialog-data.service";
import { AuthService } from "../../../auth/auth.service";
import { FilterService } from "../../services/filter-service";
@ -11,11 +15,19 @@ import { SearchV2Service } from "../../../services";
import { StorageService } from "../../../shared/storage/storage-service";
import { isEqual } from "lodash";
import { FeaturesFacade } from "../../../state/features/features.facade";
import { DiscoverCardComponent } from "../card/discover-card.component";
@Component({
standalone: false,
standalone: true,
templateUrl: "./search-results.component.html",
styleUrls: ["../discover/discover.component.scss"],
imports: [
CommonModule,
TranslateModule,
CarouselModule,
SkeletonModule,
DiscoverCardComponent
]
})
export class DiscoverSearchResultsComponent implements OnInit {

View File

@ -10,6 +10,16 @@ import { SharedModule } from "../shared/shared.module";
import { SkeletonModule } from 'primeng/skeleton';
import { ImageComponent } from 'app/components';
// Import standalone components
import { DiscoverComponent } from './components/discover/discover.component';
import { DiscoverCardComponent } from './components/card/discover-card.component';
import { DiscoverCollectionsComponent } from './components/collections/discover-collections.component';
import { DiscoverActorComponent } from './components/actor/discover-actor.component';
import { DiscoverSearchResultsComponent } from './components/search-results/search-results.component';
import { CarouselListComponent } from './components/carousel-list/carousel-list.component';
import { RecentlyRequestedListComponent } from './components/recently-requested-list/recently-requested-list.component';
import { GenreButtonSelectComponent } from './components/genre/genre-button-select.component';
@NgModule({
imports: [
RouterModule.forChild(fromComponents.routes),
@ -20,9 +30,18 @@ import { ImageComponent } from 'app/components';
InfiniteScrollModule,
SkeletonModule,
ImageComponent,
// Import standalone components
DiscoverComponent,
DiscoverCardComponent,
DiscoverCollectionsComponent,
DiscoverActorComponent,
DiscoverSearchResultsComponent,
CarouselListComponent,
RecentlyRequestedListComponent,
GenreButtonSelectComponent,
],
declarations: [
...fromComponents.components
// All components are now standalone - no declarations needed
],
exports: [
RouterModule,

View File

@ -1,5 +1,4 @@
import { Pipe, PipeTransform } from "@angular/core";
import { FormatPipe } from 'ngx-date-fns';
import { parseISO, format } from 'date-fns';
@Pipe({
@ -8,10 +7,6 @@ import { parseISO, format } from 'date-fns';
})
export class OmbiDatePipe implements PipeTransform {
constructor(
private FormatPipe: FormatPipe,
) {}
public transform(value: string, formatStr: string ) {
if (!value) {
return '';

View File

@ -1,4 +1,5 @@
<form [formGroup]="form" (ngSubmit)="onSubmit()" *ngIf="form">
@if (form) {
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<h1 id="advancedOptionsTitle">
<i class="fas fa-sliders-h"></i> {{ "Search.AdvancedSearchHeader" | translate }}
</h1>
@ -65,4 +66,5 @@
<i class="fas fa-plus"></i> {{ "Common.Search" | translate }}
</button>
</div>
</form>
</form>
}

View File

@ -2,13 +2,14 @@
<mat-form-field appearance="outline" floatLabel=auto class="example-chip-list">
<mat-label>{{ "MediaDetails.Genres" | translate }}</mat-label>
<mat-chip-list #chipList aria-label="Fruit selection">
<mat-chip
*ngFor="let word of form.controls.genreIds.value"
[removable]="true"
(removed)="remove(word)">
{{word.name}}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
@for (word of form.controls.genreIds.value; track word.id) {
<mat-chip
[removable]="true"
(removed)="remove(word)">
{{word.name}}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
}
<input
[placeholder]="'Search.SearchGenre' | translate"
#keywordInput
@ -17,9 +18,11 @@
[matChipInputFor]="chipList"/>
</mat-chip-list>
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
<mat-option *ngFor="let word of filteredKeywords | async" [value]="word">
{{word.name}}
</mat-option>
@for (word of filteredKeywords | async; track word.id) {
<mat-option [value]="word">
{{word.name}}
</mat-option>
}
</mat-autocomplete>
</mat-form-field>

View File

@ -1,13 +1,14 @@
<mat-form-field class="example-chip-list" appearance="outline" floatLabel=auto>
<mat-label>{{ 'Filter.WatchProviders' | translate }}</mat-label>
<mat-chip-list #chipList aria-label="Fruit selection">
<mat-chip
*ngFor="let word of form.controls.watchProviders.value"
[removable]="true"
(removed)="remove(word)">
{{word.provider_name}}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
@for (word of form.controls.watchProviders.value; track word.provider_id) {
<mat-chip
[removable]="true"
(removed)="remove(word)">
{{word.provider_name}}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
}
<input
[placeholder]="'Search.SearchProvider' | translate"
#keywordInput
@ -16,9 +17,11 @@
[matChipInputFor]="chipList"/>
</mat-chip-list>
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
<mat-option *ngFor="let word of filteredList | async" [value]="word">
{{word.provider_name}}
</mat-option>
@for (word of filteredList | async; track word.provider_id) {
<mat-option [value]="word">
{{word.provider_name}}
</mat-option>
}
</mat-autocomplete>
</mat-form-field>

View File

@ -1,14 +1,22 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { CommonModule } from "@angular/common";
import { TranslateModule } from "@ngx-translate/core";
import { OverlayPanelModule, OverlayPanel } from "primeng/overlaypanel";
import { TabViewModule } from "primeng/tabview";
import { OverlayPanel } from "primeng/overlaypanel";
import { NotificationService, VoteService } from "../services";
import { IVoteEngineResult, IVoteViewModel, RequestTypes, VoteType } from "../interfaces";
@Component({
standalone: false,
standalone: true,
templateUrl: "vote.component.html",
styleUrls: ["vote.component.scss"],
imports: [
CommonModule,
TranslateModule,
OverlayPanelModule,
TabViewModule
]
})
export class VoteComponent implements OnInit {

View File

@ -15,8 +15,16 @@ import { VoteComponent } from './vote.component';
const routes: Routes = [{ path: '', component: VoteComponent, canActivate: [AuthGuard] }];
@NgModule({
imports: [RouterModule.forChild(routes), OmbiShared, TabViewModule, OverlayPanelModule],
declarations: [VoteComponent],
imports: [
RouterModule.forChild(routes),
OmbiShared,
TabViewModule,
OverlayPanelModule,
VoteComponent // Import standalone component
],
declarations: [
// All components are now standalone - no declarations needed
],
exports: [RouterModule],
providers: [VoteService],
})