+
+
+
+
+ {{ movie.id }}
+
+
+
+ {{ movie.original_title | capitalizeWord }}
+
+
0"
+ class="d-inline-flex flex-column"
+ >
+
+
+
+
+
+
+
+ {{ alternative.lang }}: {{ alternative.title }}
+
+
+
+
0;
- else originalLang
+ movie.production_countries && movie.production_countries.length > 0
"
>
-
+
+
+
+
+
+
+
+
+
0;
+ else originalLang
+ "
+ >
+
+
+
+
+
-
+
-
-
-
-
-
-
-
- {{ 'release_type.' + release.type | translate }}:
- {{
- release.date
- | date: 'dd MMMM
+
+
+
+
+
+ {{ 'release_type.' + release.type | translate }}:
+ {{
+ release.date
+ | date: 'dd MMMM
yyyy':'':translate.currentLang
+ | capitalizeWord
+ }}
+
+
+
+ {{
+ movie.date
+ | date: 'EEEE dd MMMM yyyy':'':translate.currentLang
| capitalizeWord
}}
-
-
-
- {{
- movie.date
- | date: 'EEEE dd MMMM yyyy':'':translate.currentLang
- | capitalizeWord
- }}
-
-
-
- 0">
- {{ movie.budget | currency: 'USD':'':'1.0-0':translate.currentLang }}
-
-
- 0">
- {{ movie.recette | currency: 'USD':'':'1.0-0':translate.currentLang }}
-
-
-
-
- 0">
-
-
-
+
+
+
+
0">
+ {{ movie.budget | currency: 'USD':'':'1.0-0':translate.currentLang }}
+
+
+
0">
+ {{ movie.recette | currency: 'USD':'':'1.0-0':translate.currentLang }}
+
+
+
+
+
0">
+
+
{{ genre.name | capitalizeWord }}
- /
-
+ {{ genre.name | capitalizeWord }}
+ /
+
+
-
-
- 0">
-
-
-
+
+ 0">
+
+
{{ keyword.name | capitalizeWord }}
- /
-
+ {{ keyword.name | capitalizeWord }}
+ /
+
+
+
+
+
+
{{ movie.overview }}
+
+
+ 0">
+
+
+
+ 0 || movie.actors?.length > 0"
+ [crew]="movie.crew"
+ [actors]="movie.actors"
+ [isDetail]="isDetail"
+ >
+
+ 0"
+ >
+
+
-
-
-
-
{{ movie.overview }}
-
-
-
-
+
-
-
-
-
-
-
-
-
-
+
diff --git a/src/app/application-modules/movie-detail/component/movie-detail/movie-detail.component.ts b/src/app/application-modules/movie-detail/component/movie-detail/movie-detail.component.ts
index 6d11c8c66..5241ee711 100644
--- a/src/app/application-modules/movie-detail/component/movie-detail/movie-detail.component.ts
+++ b/src/app/application-modules/movie-detail/component/movie-detail/movie-detail.component.ts
@@ -1,17 +1,14 @@
-import {TranslateService, LangChangeEvent} from '@ngx-translate/core';
+import {TranslateService} from '@ngx-translate/core';
+import {Component, Input, Output, EventEmitter} from '@angular/core';
+import {ActivatedRoute, Router, Params} from '@angular/router';
import {
- Component,
- OnInit,
- OnDestroy,
- Input,
- OnChanges,
- SimpleChanges,
- Output,
- EventEmitter,
-} from '@angular/core';
-import {ActivatedRoute, Router, ParamMap} from '@angular/router';
-import {filter} from 'rxjs/operators';
-import {combineLatest, Subscription} from 'rxjs';
+ filter,
+ tap,
+ map,
+ switchMap,
+ distinctUntilChanged,
+} from 'rxjs/operators';
+import {combineLatest, merge, ReplaySubject} from 'rxjs';
import {
faImage,
faChevronCircleRight,
@@ -22,32 +19,127 @@ import {
import {Tag} from './../../../../model/tag';
import {DuckDuckGo} from './../../../../constant/duck-duck-go';
import {Movie} from '../../../../model/movie';
-import {Keyword, Genre, DetailConfig} from '../../../../model/model';
-import {MovieService} from '../../../../service/movie.service';
+import {DetailConfig, Id} from '../../../../model/model';
import {TitleService} from '../../../../service/title.service';
import {TabsService} from '../../../../service/tabs.service';
import {MyTagsService} from '../../../../service/my-tags.service';
import {MyDatasService} from '../../../../service/my-datas.service';
import {MenuService} from '../../../../service/menu.service';
+import {MovieManager} from '../../../../manager/movie.manager';
@Component({
selector: 'app-movie-detail',
styleUrls: ['./movie-detail.component.scss'],
templateUrl: './movie-detail.component.html',
})
-export class MovieDetailComponent implements OnInit, OnChanges, OnDestroy {
- @Input() id!: number;
- @Input() config!: DetailConfig;
+export class MovieDetailComponent {
+ private readonly id$ = new ReplaySubject
(1);
+ private readonly config$ = new ReplaySubject(1);
+
+ @Input()
+ set id(value: number) {
+ this.isDetail = false;
+ this.config$.next(
+ new DetailConfig(
+ true,
+ true,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ undefined
+ )
+ );
+ this.loaded.emit(false);
+ this.id$.next(value);
+ }
+
+ ids$ = merge(
+ this.movieManager.listenParam(this.route.paramMap, 'id').pipe(
+ filter(id => id !== 0),
+ tap(() => {
+ this.isDetail = true;
+ this.config$.next(
+ new DetailConfig(
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ false,
+ undefined
+ )
+ );
+ })
+ ),
+ this.id$
+ );
+
+ movie$ = this.config$.pipe(
+ distinctUntilChanged(
+ (prev, curr) =>
+ prev.similar === curr.similar && prev.keywords === curr.keywords
+ ),
+ switchMap(config =>
+ this.movieManager.find(this.ids$, config).pipe(
+ tap(movie => {
+ this.loaded.emit(true);
+ if (this.isDetail) {
+ this.title.setTitle(movie.title);
+ this.menuService.scrollTo$.next(0);
+ }
+ })
+ )
+ )
+ );
+
+ loading$ = combineLatest([this.ids$, this.movie$]).pipe(
+ map(([i, m]) => !m || i !== m.id)
+ );
+
+ tags$ = combineLatest([
+ this.myTagsService.myTags$,
+ this.myDatasService.myMovies$,
+ this.movie$,
+ ]).pipe(
+ filter(
+ ([tags, movies, movie]) =>
+ tags !== undefined &&
+ tags.length > 0 &&
+ movies !== undefined &&
+ movie !== undefined
+ ),
+ map(([tags, movies, movie]) => {
+ this.showTags = false;
+ if (movies.map(m => m.id).includes(movie.id)) {
+ this.showTags = true;
+ return tags.filter(t =>
+ t.datas
+ .filter(d => !d.movie)
+ .map(d => d.id)
+ .includes(movie.id)
+ );
+ } else {
+ return [];
+ }
+ })
+ );
+
@Output() loaded = new EventEmitter();
movie!: Movie;
tags: Tag[] = [];
showTags = false;
isImagesVisible = false;
- isDetail!: boolean;
+ isDetail: boolean;
showTitles = false;
- sc!: string;
+ sc: string;
Url = DuckDuckGo;
- subs: Subscription[] = [];
faChevronCircleRight = faChevronCircleRight;
faImage = faImage;
@@ -55,9 +147,9 @@ export class MovieDetailComponent implements OnInit, OnChanges, OnDestroy {
faMinus = faMinus;
constructor(
- private movieService: MovieService,
+ private movieManager: MovieManager,
private route: ActivatedRoute,
- private translate: TranslateService,
+ protected translate: TranslateService,
private title: TitleService,
private router: Router,
public tabsService: TabsService,
@@ -66,101 +158,11 @@ export class MovieDetailComponent implements OnInit, OnChanges, OnDestroy {
private myDatasService: MyDatasService
) {}
- ngOnInit(): void {
- this.subs.push(
- this.route.paramMap.subscribe((params: ParamMap) => {
- if (params) {
- const idParam = +params.get('id');
- if (idParam && idParam !== 0) {
- this.id = idParam;
- this.isDetail = true;
- this.getMovie(this.id);
- }
- }
- })
- );
- this.subs.push(
- this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
- this.config.lang = event.lang;
- this.getMovie(this.id);
- })
- );
- }
-
- ngOnChanges(changes: SimpleChanges): void {
- if (changes.id) {
- this.id = changes.id.currentValue ? changes.id.currentValue : this.id;
- this.isDetail = false;
- }
- this.getMovie(this.id);
- }
-
- getMovie(id: number): void {
- if (this.id && this.id !== 0) {
- this.loaded.emit(false);
- this.config =
- this.config === undefined
- ? new DetailConfig(
- true,
- true,
- true,
- true,
- true,
- true,
- true,
- true,
- false,
- this.translate.currentLang
- )
- : this.config;
- this.movieService.getMovie(id, this.config, true).then(movie => {
- this.movie = movie;
- this.loaded.emit(true);
- if (this.isDetail) {
- this.title.setTitle(movie.title);
- this.menuService.scrollTo$.next(0);
- }
- });
- this.subs.push(
- combineLatest([
- this.myTagsService.myTags$,
- this.myDatasService.myMovies$,
- ])
- .pipe(
- filter(
- ([tags, movies]) => tags !== undefined && movies !== undefined
- )
- )
- .subscribe(([tags, movies]) => {
- this.tags = [];
- this.showTags = false;
- if (movies.map(m => m.id).includes(this.id)) {
- this.showTags = true;
- this.tags = tags.filter(t =>
- t.datas
- .filter(d => d.movie)
- .map(m => m.id)
- .includes(this.id)
- );
- }
- })
- );
- }
- }
-
- redirectGenreToDiscover(genre: Genre): void {
+ toDiscover(item: T, key: string): void {
+ const params: Params = {};
+ params[key] = JSON.stringify([item.id]);
this.router.navigate(['discover'], {
- queryParams: {genre: JSON.stringify([genre.id])},
+ queryParams: params,
});
}
-
- redirectKeywordToDiscover(keyword: Keyword): void {
- this.router.navigate(['discover'], {
- queryParams: {keyword: JSON.stringify([keyword.id])},
- });
- }
-
- ngOnDestroy(): void {
- this.subs.forEach(subscription => subscription.unsubscribe());
- }
}
diff --git a/src/app/application-modules/release/component/release.component.html b/src/app/application-modules/release/component/release.component.html
index 3c090d46e..de29e0a99 100644
--- a/src/app/application-modules/release/component/release.component.html
+++ b/src/app/application-modules/release/component/release.component.html
@@ -63,7 +63,6 @@
diff --git a/src/app/application-modules/serie-detail/season-detail/season-detail.component.ts b/src/app/application-modules/serie-detail/season-detail/season-detail.component.ts
index c0cb706d6..21e348d58 100644
--- a/src/app/application-modules/serie-detail/season-detail/season-detail.component.ts
+++ b/src/app/application-modules/serie-detail/season-detail/season-detail.component.ts
@@ -28,16 +28,18 @@ export class SeasonDetailComponent {
faArrowCircleRight = faArrowCircleRight;
serie$ = this.serieManager
- .find(this.route.paramMap, 'id')
+ .find(this.serieManager.listenParam(this.route.paramMap, 'id'))
.pipe(tap(s => this.title.setTitle(s.title)));
- season$ = this.seasonManager.find(this.route.paramMap, 'season').pipe(
- map(season => {
- season.images.push(...season.episodes.map(e => e.poster));
- season.images = season.images.filter(i => Utils.isNotBlank(i));
- return season;
- })
- );
+ season$ = this.seasonManager
+ .find(this.serieManager.listenParam(this.route.paramMap, 'season'))
+ .pipe(
+ map(season => {
+ season.images.push(...season.episodes.map(e => e.poster));
+ season.images = season.images.filter(i => Utils.isNotBlank(i));
+ return season;
+ })
+ );
constructor(
private serieManager: SerieManager,
diff --git a/src/app/application-modules/serie-detail/serie-detail/serie-detail.component.ts b/src/app/application-modules/serie-detail/serie-detail/serie-detail.component.ts
index 5defdcdea..b401f26d4 100644
--- a/src/app/application-modules/serie-detail/serie-detail/serie-detail.component.ts
+++ b/src/app/application-modules/serie-detail/serie-detail/serie-detail.component.ts
@@ -33,12 +33,14 @@ export class SerieDetailComponent {
imageSize = ImageSize;
protected sc!: string;
- serie$ = this.serieManager.find(this.route.paramMap, 'id').pipe(
- tap(serie => {
- this.title.setTitle(serie.title);
- this.menuService.scrollTo$.next(0);
- })
- );
+ serie$ = this.serieManager
+ .find(this.serieManager.listenParam(this.route.paramMap, 'id'))
+ .pipe(
+ tap(serie => {
+ this.title.setTitle(serie.title);
+ this.menuService.scrollTo$.next(0);
+ })
+ );
loading$ = combineLatest([
this.serieManager.listenParam(this.route.paramMap, 'id'),
diff --git a/src/app/manager/abstract.manager.ts b/src/app/manager/abstract.manager.ts
index 5f0468f47..7d60e00c8 100644
--- a/src/app/manager/abstract.manager.ts
+++ b/src/app/manager/abstract.manager.ts
@@ -9,6 +9,7 @@ import {
map,
startWith,
} from 'rxjs/operators';
+import {DetailConfig} from '../model/model';
export abstract class AbstractService {
private readonly id$ = new BehaviorSubject(undefined);
@@ -51,8 +52,7 @@ export abstract class AbstractService {
}
public abstract find(
- paramMap: Observable,
- key: string,
- ...args: (number | string)[]
+ id$: Observable,
+ ...args: DetailConfig[]
): Observable;
}
diff --git a/src/app/manager/movie.manager.ts b/src/app/manager/movie.manager.ts
new file mode 100644
index 000000000..b36f8d536
--- /dev/null
+++ b/src/app/manager/movie.manager.ts
@@ -0,0 +1,40 @@
+import {Injectable} from '@angular/core';
+import {AbstractService} from './abstract.manager';
+import {DetailConfig} from '../model/model';
+import {TranslateService} from '@ngx-translate/core';
+import {Observable, combineLatest} from 'rxjs';
+import {switchMap, tap} from 'rxjs/operators';
+import {MovieService} from '../service/movie.service';
+import {Movie} from '../model/movie';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class MovieManager extends AbstractService<
+ Movie,
+ {id: number; config: DetailConfig}
+> {
+ constructor(
+ private readonly movieService: MovieService,
+ translate: TranslateService
+ ) {
+ super(
+ movieId => this.movieService.getMovie$(movieId.id, movieId.config, true),
+ (prev, curr) =>
+ prev.id === curr.id &&
+ prev.config.lang === curr.config.lang &&
+ prev.config.reco === curr.config.reco,
+ translate
+ );
+ }
+
+ find(id$: Observable, config: DetailConfig): Observable {
+ return combineLatest([id$, this.lang$]).pipe(
+ tap(([id, lang]) => {
+ config.lang = lang;
+ this.update({id, config});
+ }),
+ switchMap(() => this.listen())
+ );
+ }
+}
diff --git a/src/app/manager/season.manager.ts b/src/app/manager/season.manager.ts
index 10f6d227c..724cef00d 100644
--- a/src/app/manager/season.manager.ts
+++ b/src/app/manager/season.manager.ts
@@ -4,7 +4,6 @@ import {Season, SeasonId} from '../model/season';
import {TranslateService} from '@ngx-translate/core';
import {SerieService} from '../service/serie.service';
import {Observable, combineLatest} from 'rxjs';
-import {ParamMap} from '@angular/router';
import {switchMap, tap} from 'rxjs/operators';
import {SerieManager} from './serie.manager';
@@ -30,12 +29,8 @@ export class SeasonManager extends AbstractService {
);
}
- find(paramMap: Observable, key: string): Observable {
- return combineLatest([
- this.listenParam(paramMap, key),
- this.lang$,
- this.serieManager.listen(),
- ]).pipe(
+ find(id$: Observable): Observable {
+ return combineLatest([id$, this.lang$, this.serieManager.listen()]).pipe(
tap(([id, lang, serie]) =>
this.update({id: id, serieId: serie.id, lang: lang})
),
diff --git a/src/app/manager/serie.manager.ts b/src/app/manager/serie.manager.ts
index 2aad68e2e..accd0b2c5 100644
--- a/src/app/manager/serie.manager.ts
+++ b/src/app/manager/serie.manager.ts
@@ -5,7 +5,6 @@ import {SerieService} from '../service/serie.service';
import {DetailConfig} from '../model/model';
import {TranslateService} from '@ngx-translate/core';
import {Observable, combineLatest} from 'rxjs';
-import {ParamMap} from '@angular/router';
import {switchMap, tap} from 'rxjs/operators';
@Injectable({
@@ -39,8 +38,8 @@ export class SerieManager extends AbstractService {
);
}
- find(paramMap: Observable, key: string): Observable {
- return combineLatest([this.listenParam(paramMap, key), this.lang$]).pipe(
+ find(id$: Observable): Observable {
+ return combineLatest([id$, this.lang$]).pipe(
tap(([id, lang]) => this.update({id, lang})),
switchMap(() => this.listen())
);
diff --git a/src/app/service/movie.service.ts b/src/app/service/movie.service.ts
index d24145801..4ecb61103 100644
--- a/src/app/service/movie.service.ts
+++ b/src/app/service/movie.service.ts
@@ -1,4 +1,4 @@
-import {forkJoin} from 'rxjs';
+import {Observable, forkJoin, iif} from 'rxjs';
import {Injectable} from '@angular/core';
import {DiscoverCriteria} from '../model/discover-criteria';
@@ -13,6 +13,7 @@ import {OmdbService} from './omdb.service';
import {ToastService} from './toast.service';
import {UrlBuilder} from '../shared/urlBuilder';
import {Utils} from '../shared/utils';
+import {map, mergeMap, catchError} from 'rxjs/operators';
@Injectable({
providedIn: 'root',
@@ -57,8 +58,16 @@ export class MovieService {
}
getMovie(id: number, config: DetailConfig, detail: boolean): Promise {
+ return this.getMovie$(id, config, detail).toPromise();
+ }
+
+ getMovie$(
+ id: number,
+ config: DetailConfig,
+ detail: boolean
+ ): Observable {
return this.serviceUtils
- .getPromise(
+ .getObservable(
UrlBuilder.detailUrlBuilder(
true,
id,
@@ -74,58 +83,56 @@ export class MovieService {
config.lang
)
)
- .then(response => {
- const movie = MapMovie.mapForMovie(response, this.mockService);
- movie.lang_version = config.lang ?? movie.lang_version;
- if (
- detail &&
- (!movie.overview ||
- ((movie.videos === undefined || movie.videos.length === 0) &&
- config.video) ||
- !movie.original_title)
- ) {
- return this.serviceUtils
- .getPromise(
- UrlBuilder.detailUrlBuilder(
- true,
- id,
+ .pipe(
+ map(response => {
+ const movie = MapMovie.mapForMovie(response, this.mockService);
+ movie.lang_version = config.lang ?? movie.lang_version;
+ return movie;
+ }),
+ mergeMap(movie =>
+ iif(
+ () =>
+ detail &&
+ (!movie.overview ||
+ ((movie.videos === undefined || movie.videos.length === 0) &&
+ config.video) ||
+ !movie.original_title),
+ this.getMovie$(
+ id,
+ new DetailConfig(
+ false,
+ false,
+ false,
+ false,
config.video,
- undefined,
- undefined,
- undefined,
- undefined,
- undefined,
- undefined,
- undefined,
+ false,
+ false,
+ false,
false,
'en'
- )
- )
- .then(enMovie => {
- const resp = MapMovie.mapForMovie(enMovie, this.mockService);
- resp.lang_version = config.lang ?? movie.lang_version;
- return resp;
- })
- .then(enMovie => {
- movie.overview = Utils.isBlank(movie.overview)
- ? enMovie.overview
- : movie.overview;
- movie.videos =
- movie.videos && movie.videos.length > 0
- ? movie.videos
- : enMovie.videos;
- movie.original_title = Utils.isBlank(movie.original_title)
- ? enMovie.original_title
- : movie.original_title;
- movie.score = enMovie.score;
- return movie;
- });
- } else {
- return movie;
- }
- })
- .then(movie => this.omdb.getImdbScore(movie).toPromise())
- .catch(err => this.serviceUtils.handlePromiseError(err, this.toast));
+ ),
+ false
+ ).pipe(
+ map(enMovie => {
+ movie.overview = Utils.isBlank(movie.overview)
+ ? enMovie.overview
+ : movie.overview;
+ movie.videos =
+ movie.videos && movie.videos.length > 0
+ ? movie.videos
+ : enMovie.videos;
+ movie.original_title = Utils.isBlank(movie.original_title)
+ ? enMovie.original_title
+ : movie.original_title;
+ movie.score = enMovie.score;
+ return movie;
+ })
+ ),
+ this.omdb.getImdbScore(movie).toPromise()
+ )
+ ),
+ catchError(err => this.serviceUtils.handleObsError(err, this.toast))
+ );
}
getMoviesByReleaseDates(