Finishing the show details layout for the web app.

This commit is contained in:
Zoe Roux 2019-09-02 21:55:20 +02:00
parent bc695560ae
commit 8d063489d9
15 changed files with 149 additions and 27 deletions

View File

@ -6,18 +6,21 @@ import { ShowDetailsComponent } from './show-details/show-details.component';
import { NotFoundComponent } from './not-found/not-found.component'; import { NotFoundComponent } from './not-found/not-found.component';
import { ShowResolverService } from './services/show-resolver.service'; import { ShowResolverService } from './services/show-resolver.service';
import { LibraryResolverService } from './services/library-resolver.service'; import { LibraryResolverService } from './services/library-resolver.service';
import { PlayerComponent } from "./player/player.component";
import { StreamResolverService } from "./services/stream-resolver.service";
const routes: Routes = [ const routes: Routes = [
{ path: "browse", component: BrowseComponent, pathMatch: "full", resolve: { shows: LibraryResolverService } }, { path: "browse", component: BrowseComponent, pathMatch: "full", resolve: { shows: LibraryResolverService } },
{ path: "browse/:library-slug", component: BrowseComponent, resolve: { shows: LibraryResolverService } }, { path: "browse/:library-slug", component: BrowseComponent, resolve: { shows: LibraryResolverService } },
{ path: "shows/:show-slug", component: ShowDetailsComponent, resolve: { show: ShowResolverService } }, { path: "shows/:show-slug", component: ShowDetailsComponent, resolve: { show: ShowResolverService } },
{ path: "watch/:show-slug/s:season-number/e:episode-number", component: PlayerComponent, resolve: { show: StreamResolverService } },
{ path: "**", component: NotFoundComponent } { path: "**", component: NotFoundComponent }
]; ];
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes)], imports: [RouterModule.forRoot(routes)],
exports: [RouterModule], exports: [RouterModule],
providers: [LibraryResolverService, ShowResolverService] providers: [LibraryResolverService, ShowResolverService, StreamResolverService]
}) })
export class AppRoutingModule { } export class AppRoutingModule { }

View File

@ -16,10 +16,10 @@
<ul class="navbar-nav flex-row ml-auto"> <ul class="navbar-nav flex-row ml-auto">
<li class="nav-item icon"> <li class="nav-item icon">
<mat-icon>search</mat-icon> <mat-icon data-toggle="tooltip" data-placement="top" title="Search">search</mat-icon>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="icon" routerLink="/login" routerLinkActive="active"> <a class="icon" routerLink="/login" routerLinkActive="active" data-toggle="tooltip" data-placement="top" title="Login">
<mat-icon>account_circle</mat-icon> <mat-icon>account_circle</mat-icon>
</a> </a>
</li> </li>

View File

@ -14,6 +14,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { EpisodesListComponent } from './episodes-list/episodes-list.component'; import { EpisodesListComponent } from './episodes-list/episodes-list.component';
import { PlayerComponent } from './player/player.component';
@NgModule({ @NgModule({
@ -22,7 +23,8 @@ import { EpisodesListComponent } from './episodes-list/episodes-list.component';
NotFoundComponent, NotFoundComponent,
BrowseComponent, BrowseComponent,
ShowDetailsComponent, ShowDetailsComponent,
EpisodesListComponent EpisodesListComponent,
PlayerComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -61,6 +61,7 @@
text-overflow: ellipsis; text-overflow: ellipsis;
text-align: center; text-align: center;
margin-bottom: 0px; margin-bottom: 0px;
opacity: 1;
&.date &.date
{ {

View File

@ -1,10 +1,11 @@
<div class="root"> <div class="root">
<div class="episodes" id="episodes"> <div class="episodes" id="episodes">
<div class="episode" *ngFor="let episode of this.episodes" id="el-{{episode.episodeNumber}}"> <div class="episode" *ngFor="let episode of this.episodes" id="el-{{episode.episodeNumber}}">
<div class="img" [style.background-image]="sanitize(episode.thumb)"> <div class="img" [style.background-image]="sanitize(episode.thumb, true)">
<button mat-icon-button id="playBtn"><i class="material-icons playIcon">play_circle_outline</i></button> <button mat-icon-button class="playBtn" ><i class="material-icons playIcon">play_circle_outline</i></button>
</div> </div>
<p class="title">S{{episode.seasonNumber}}E{{episode.episodeNumber}} - {{episode.title}}</p> <h6 *ngIf="episode.seasonNumber != 0; else elseBlock;" class="title">S{{episode.seasonNumber}}:E{{episode.episodeNumber}} - {{episode.title}}</h6>
<ng-template #elseBlock><h6 class="title">{{episode.title}}</h6></ng-template>
<p class="overview">{{episode.overview}}</p> <p class="overview">{{episode.overview}}</p>
</div> </div>
</div> </div>

View File

@ -104,6 +104,7 @@
.title .title
{ {
padding-top: .2rem;
font-weight: 600; font-weight: 600;
margin-bottom: 0; margin-bottom: 0;
display: -webkit-box; display: -webkit-box;
@ -126,17 +127,17 @@
.img .img
{ {
outline: solid var(--accentColor); outline: solid var(--accentColor);
.playBtn
{
display: block;
}
} }
.title .title
{ {
text-decoration: underline; text-decoration: underline;
} }
#playBtn
{
display: block;
}
} }
} }

View File

@ -55,4 +55,9 @@ export class EpisodesListComponent implements OnInit
return offset; return offset;
} }
play(episode: Episode)
{
}
} }

View File

@ -0,0 +1 @@
<p>player works!</p>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PlayerComponent } from './player.component';
describe('PlayerComponent', () => {
let component: PlayerComponent;
let fixture: ComponentFixture<PlayerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PlayerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PlayerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-player',
templateUrl: './player.component.html',
styleUrls: ['./player.component.scss']
})
export class PlayerComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, EMPTY } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Show } from "../../models/show";
import { MatSnackBar } from '@angular/material/snack-bar';
@Injectable()
export class StreamResolverService implements Resolve<Show>
{
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
resolve(route: ActivatedRouteSnapshot): Show | Observable<Show> | Promise<Show>
{
let slug: string = route.paramMap.get("show-slug");
let season: number = parseInt(route.paramMap.get("season-number"));
let episode: number = parseInt(route.paramMap.get("episode-number"));
return this.http.get<Show>("api/watch/" + slug + "/s" + season + "/e" + episode).pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
if (error.status == 404)
{
this.snackBar.open("Can't find this episode \"" + slug + "S" + season + ":E" + episode + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
else
{
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
return EMPTY;
}));
}
}

View File

@ -1,4 +1,6 @@
<img id="backdrop" class="backdrop" src="backdrop/{{this.show.slug}}" /> <div class="backdrop">
<img id="backdrop" src="backdrop/{{this.show.slug}}" />
</div>
<div class="header container pt-sm-5"> <div class="header container pt-sm-5">
<div class="row"> <div class="row">
@ -53,9 +55,9 @@
</div> </div>
<hr class="d-none d-sm-block"> <hr class="d-none d-sm-block">
<div class="col-3 d-none d-sm-block"> <div class="col-3 d-none d-sm-block">
<h3>Genres</h3> <h3 style="opacity: .8;">Genres</h3>
<ul> <ul>
<li *ngFor="let genre of this.show.genres"><b><a routerLink="/genre/{{genre.slug}}">{{genre.name}}</a></b></li> <li *ngFor="let genre of this.show.genres"><b><a class="genre" routerLink="/genre/{{genre.slug}}">{{genre.name}}</a></b></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -80,7 +82,7 @@
<div class="people-container" id="peopleScroll"> <div class="people-container" id="peopleScroll">
<a class="people" *ngFor="let people of this.show.people" routerLink="/people/{{people.slug}}"> <a class="people" *ngFor="let people of this.show.people" routerLink="/people/{{people.slug}}">
<img [style.background-image]="getPeopleIcon(people.slug)" /> <img [style.background-image]="getPeopleIcon(people.slug)" />
<p class="name">{{people.name}}</p> <h6 class="name">{{people.name}}</h6>
<p class="role">{{people.role}}</p> <p class="role">{{people.role}}</p>
</a> </a>
</div> </div>

View File

@ -9,22 +9,39 @@ a
.backdrop .backdrop
{ {
width: 100%;
margin-top: -68px; margin-top: -68px;
position: relative;
z-index: -1;
> img
{
width: 100%;
max-height: 75vh; max-height: 75vh;
object-fit: cover; object-fit: cover;
} }
&:after
{
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.6) 100%);
}
}
.header .header
{ {
@include media-breakpoint-up(sm) @include media-breakpoint-up(sm)
{ {
margin-top: -14rem; margin-top: -12rem;
} }
@include media-breakpoint-up(md) @include media-breakpoint-up(md)
{ {
margin-top: -14rem; margin-top: -13rem;
} }
@include media-breakpoint-up(lg) @include media-breakpoint-up(lg)
@ -98,10 +115,17 @@ a
position: absolute; position: absolute;
bottom: 0; bottom: 0;
} }
> div > p
{
opacity: .87;
}
} }
.overview .overview
{ {
opacity: .87;
@include media-breakpoint-up(sm) @include media-breakpoint-up(sm)
{ {
padding-top: 2.25rem; padding-top: 2.25rem;
@ -116,6 +140,11 @@ hr
height: inherit; height: inherit;
} }
.genre
{
opacity: .8;
}
.scroll-row .scroll-row
{ {
position: relative; position: relative;
@ -229,7 +258,7 @@ hr
background-color: #333333; background-color: #333333;
} }
> p > p, h6
{ {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
@ -239,7 +268,6 @@ hr
&.role &.role
{ {
opacity: 0.8;
font-size: 0.8em; font-size: 0.8em;
} }
} }

View File

@ -16,11 +16,15 @@ $body-bg: theme-color("primary");
$body-color: theme-color("textPrimary"); $body-color: theme-color("textPrimary");
$font-family-base: "Roboto", Arial, sans-serif; $font-family-base: "Roboto", Arial, sans-serif;
/*body p
{ {
background-repeat: no-repeat; opacity: .6;
background-attachment: fixed; }
}*/
h6
{
opacity: .87;
}
@import "~bootstrap/scss/bootstrap"; @import "~bootstrap/scss/bootstrap";