mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-07 10:14:13 -04:00
Search view created.
This commit is contained in:
parent
c84eac21d2
commit
4fb0274410
@ -30,6 +30,13 @@ const routes: Routes = [
|
||||
scrollPositionRestoration: "enabled"
|
||||
})],
|
||||
exports: [RouterModule],
|
||||
providers: [LibraryResolverService, ShowResolverService, StreamResolverService]
|
||||
providers: [
|
||||
LibraryResolverService,
|
||||
ShowResolverService,
|
||||
CollectionResolverService,
|
||||
PeopleResolverService,
|
||||
StreamResolverService,
|
||||
SearchResolverService
|
||||
]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<ul class="navbar-nav flex-row ml-auto">
|
||||
<li class="nav-item icon searchbar">
|
||||
<input placeholder="Search" id="search" type="search" (oninit)="onUpdateValue(this.value)"/>
|
||||
<input placeholder="Search" id="search" type="search" (input)="onUpdateValue($event)"/>
|
||||
<mat-icon matTooltipPosition="below" matTooltip="Search" (click)="openSearch()">search</mat-icon>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
|
@ -66,6 +66,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
input::-webkit-search-cancel-button
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.icon
|
||||
{
|
||||
padding: 8px;
|
||||
|
@ -48,9 +48,10 @@ export class AppComponent
|
||||
input.focus();
|
||||
}
|
||||
|
||||
onUpdateValue(value: string)
|
||||
onUpdateValue(event)
|
||||
{
|
||||
console.log("Value: " + value);
|
||||
console.log("Value: " + event.target.value);
|
||||
this.router.navigate(["/search/" + event.target.value]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@ import { NotFoundComponent } from './not-found/not-found.component';
|
||||
import { PlayerComponent } from './player/player.component';
|
||||
import { ShowDetailsComponent } from './show-details/show-details.component';
|
||||
import { SearchComponent } from './search/search.component';
|
||||
import { PeopleListComponent } from './people-list/people-list.component';
|
||||
import { ShowsListComponent } from './shows-list/shows-list.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -31,7 +33,9 @@ import { SearchComponent } from './search/search.component';
|
||||
EpisodesListComponent,
|
||||
PlayerComponent,
|
||||
CollectionComponent,
|
||||
SearchComponent
|
||||
SearchComponent,
|
||||
PeopleListComponent,
|
||||
ShowsListComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -1,12 +1,19 @@
|
||||
<div class="root">
|
||||
<div class="episodes" id="episodes">
|
||||
<a class="episode" *ngFor="let episode of this.episodes" id="el-{{episode.episodeNumber}}" routerLink="/watch/{{this.showSlug}}-s{{episode.seasonNumber}}e{{episode.episodeNumber}}" href="/watch/{{this.showSlug}}-s{{episode.seasonNumber}}e{{episode.episodeNumber}}">
|
||||
<a class="episode" *ngFor="let episode of this.episodes; let i = index" id="el-{{i}}" routerLink="/watch/{{episode.link}}" href="/watch/{{this.showSlug}}-s{{episode.seasonNumber}}e{{episode.episodeNumber}}">
|
||||
<div matRipple class="img" [style.background-image]="sanitize(episode.thumb)">
|
||||
<button mat-icon-button class="playBtn"><i class="material-icons playIcon">play_circle_outline</i></button>
|
||||
</div>
|
||||
<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>
|
||||
<ng-container *ngIf="displayShowTitle; else noTitle;">
|
||||
<h6 *ngIf="episode.seasonNumber != 0; else elseBlock;" class="title">{{episode.showTitle}} - S{{episode.seasonNumber}}:E{{episode.episodeNumber}}</h6>
|
||||
<ng-template #elseBlock><h6 class="title">{{episode.showTitle}}</h6></ng-template>
|
||||
<p class="subtitle">{{episode.title}}</p>
|
||||
</ng-container>
|
||||
<ng-template #noTitle>
|
||||
<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>
|
||||
</ng-template>
|
||||
</a>
|
||||
</div>
|
||||
<button mat-raised-button color="accent" class="scrollBtn d-none" id="el-leftBtn" (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
|
||||
|
@ -118,6 +118,15 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.subtitle
|
||||
{
|
||||
font-weight: 300;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&:hover
|
||||
{
|
||||
.img
|
||||
|
@ -9,8 +9,8 @@ import { DomSanitizer } from "@angular/platform-browser";
|
||||
})
|
||||
export class EpisodesListComponent implements OnInit
|
||||
{
|
||||
@Input() displayShowTitle: boolean = false;
|
||||
@Input() episodes: Episode[];
|
||||
@Input() showSlug: string;
|
||||
private root: HTMLElement;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) { }
|
||||
|
@ -0,0 +1,11 @@
|
||||
<div class="scroll-row mb-5">
|
||||
<div class="people-container" id="peopleScroll">
|
||||
<a class="people" *ngFor="let people of this.people" routerLink="/people/{{people.slug}}" href="/people/{{people.slug}}">
|
||||
<div matRipple [style.background-image]="getPeopleIcon(people.slug)"> </div>
|
||||
<h6 class="name">{{people.name}}</h6>
|
||||
<p class="role">{{people.role}}</p>
|
||||
</a>
|
||||
</div>
|
||||
<button mat-raised-button color="accent" class="scrollBtn d-none" id="pl-leftBtn" (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
|
||||
<button mat-raised-button color="accent" class="scrollBtn" id="pl-rightBtn" (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
|
||||
</div>
|
155
Kyoo/ClientApp/src/app/people-list/people-list.component.scss
Normal file
155
Kyoo/ClientApp/src/app/people-list/people-list.component.scss
Normal file
@ -0,0 +1,155 @@
|
||||
@import "~bootstrap/scss/functions";
|
||||
@import "~bootstrap/scss/variables";
|
||||
@import "~bootstrap/scss/mixins/breakpoints";
|
||||
|
||||
.people-container
|
||||
{
|
||||
display: flex;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
overflow-x: auto;
|
||||
min-width: 100%;
|
||||
flex-shrink: 0;
|
||||
flex-direction: row;
|
||||
|
||||
&::-webkit-scrollbar
|
||||
{
|
||||
height: 4px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #999;
|
||||
border-radius: 90px;
|
||||
|
||||
&:hover
|
||||
{
|
||||
background-color: rgb(134, 127, 127);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.people
|
||||
{
|
||||
visibility: visible;
|
||||
margin: .25rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
width: 33%;
|
||||
|
||||
@include media-breakpoint-up(sm)
|
||||
{
|
||||
width: 22%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md)
|
||||
{
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg)
|
||||
{
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl)
|
||||
{
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
> div
|
||||
{
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-top: 147.0588%;
|
||||
background-size: cover;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
> p, h6
|
||||
{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
margin-bottom: 0px;
|
||||
|
||||
&.role
|
||||
{
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover
|
||||
{
|
||||
cursor: pointer;
|
||||
|
||||
> img
|
||||
{
|
||||
outline: solid var(--accentColor);
|
||||
}
|
||||
|
||||
.name
|
||||
{
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-row
|
||||
{
|
||||
position: relative;
|
||||
|
||||
&:hover
|
||||
{
|
||||
.scrollBtn
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scrollBtn
|
||||
{
|
||||
padding: 0;
|
||||
outline: none;
|
||||
min-width: 0;
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
bottom: 40%;
|
||||
display: none;
|
||||
|
||||
&#pl-leftBtn
|
||||
{
|
||||
left: 0;
|
||||
padding-left: 10px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
&#pl-rightBtn
|
||||
{
|
||||
right: 0;
|
||||
padding-right: 10px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
#leftBtn
|
||||
{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 33%;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#rightBtn
|
||||
{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 33%;
|
||||
outline: none;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PeopleListComponent } from './people-list.component';
|
||||
|
||||
describe('PeopleListComponent', () => {
|
||||
let component: PeopleListComponent;
|
||||
let fixture: ComponentFixture<PeopleListComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ PeopleListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PeopleListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
48
Kyoo/ClientApp/src/app/people-list/people-list.component.ts
Normal file
48
Kyoo/ClientApp/src/app/people-list/people-list.component.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { People } from "../../models/people";
|
||||
|
||||
@Component({
|
||||
selector: 'app-people-list',
|
||||
templateUrl: './people-list.component.html',
|
||||
styleUrls: ['./people-list.component.scss']
|
||||
})
|
||||
export class PeopleListComponent implements OnInit
|
||||
{
|
||||
@Input() people: People[];
|
||||
private peopleScroll: HTMLElement;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) { }
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.peopleScroll = document.getElementById("peopleScroll");
|
||||
}
|
||||
|
||||
scrollLeft()
|
||||
{
|
||||
let scroll: number = this.peopleScroll.offsetWidth * 0.80;
|
||||
this.peopleScroll.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
|
||||
|
||||
document.getElementById("pl-rightBtn").classList.remove("d-none");
|
||||
|
||||
if (this.peopleScroll.scrollLeft - scroll <= 0)
|
||||
document.getElementById("pl-leftBtn").classList.add("d-none");
|
||||
}
|
||||
|
||||
scrollRight()
|
||||
{
|
||||
let scroll: number = this.peopleScroll.offsetWidth * 0.80;
|
||||
this.peopleScroll.scrollBy({ top: 0, left: scroll, behavior: "smooth" });
|
||||
console.log(document.getElementById("pl-leftBtn"));
|
||||
document.getElementById("pl-leftBtn").classList.remove("d-none");
|
||||
|
||||
if (this.peopleScroll.scrollLeft + scroll >= this.peopleScroll.scrollWidth - this.peopleScroll.clientWidth)
|
||||
document.getElementById("pl-rightBtn").classList.add("d-none");
|
||||
}
|
||||
|
||||
getPeopleIcon(slug: string)
|
||||
{
|
||||
return this.sanitizer.bypassSecurityTrustStyle("url(/peopleimg/" + slug + ")");
|
||||
}
|
||||
}
|
@ -1 +1,12 @@
|
||||
<p>search works!</p>
|
||||
<div class="container-fluid mt-3">
|
||||
<h3>Shows</h3>
|
||||
</div>
|
||||
<app-shows-list [shows]="items.shows"></app-shows-list>
|
||||
<div class="container-fluid mt-5">
|
||||
<h3>Episodes</h3>
|
||||
</div>
|
||||
<app-episodes-list displayShowTitle="true" [episodes]="items.episodes"></app-episodes-list>
|
||||
<div class="container-fluid mt-5">
|
||||
<h3>People</h3>
|
||||
</div>
|
||||
<app-people-list [people]="items.people"></app-people-list>
|
||||
|
@ -15,6 +15,9 @@ export class SearchComponent implements OnInit
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.items = this.route.snapshot.data.items;
|
||||
this.route.data.subscribe((data) =>
|
||||
{
|
||||
this.items = data.items;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -73,19 +73,9 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<app-episodes-list [showSlug]="this.show.slug" [episodes]="episodes"></app-episodes-list>
|
||||
<app-episodes-list [episodes]="episodes"></app-episodes-list>
|
||||
|
||||
<div class="container-fluid mt-5">
|
||||
<h3>Staff</h3>
|
||||
</div>
|
||||
<div class="scroll-row mb-5">
|
||||
<div class="people-container" id="peopleScroll">
|
||||
<a class="people" *ngFor="let people of this.show.people" routerLink="/people/{{people.slug}}">
|
||||
<div matRipple [style.background-image]="getPeopleIcon(people.slug)" > </div>
|
||||
<h6 class="name">{{people.name}}</h6>
|
||||
<p class="role">{{people.role}}</p>
|
||||
</a>
|
||||
</div>
|
||||
<button mat-raised-button color="accent" class="scrollBtn d-none" id="pl-leftBtn" (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
|
||||
<button mat-raised-button color="accent" class="scrollBtn" id="pl-rightBtn" (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
|
||||
</div>
|
||||
<app-people-list [people]="show.people"></app-people-list>
|
||||
|
@ -150,156 +150,3 @@ hr
|
||||
{
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.scroll-row
|
||||
{
|
||||
position: relative;
|
||||
|
||||
&:hover
|
||||
{
|
||||
.scrollBtn
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scrollBtn
|
||||
{
|
||||
padding: 0;
|
||||
outline: none;
|
||||
min-width: 0;
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
bottom: 40%;
|
||||
display: none;
|
||||
|
||||
&#pl-leftBtn
|
||||
{
|
||||
left: 0;
|
||||
padding-left: 10px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
&#pl-rightBtn
|
||||
{
|
||||
right: 0;
|
||||
padding-right: 10px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.people-container
|
||||
{
|
||||
display: flex;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
overflow-x: auto;
|
||||
min-width: 100%;
|
||||
flex-shrink: 0;
|
||||
flex-direction: row;
|
||||
|
||||
&::-webkit-scrollbar
|
||||
{
|
||||
height: 4px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #999;
|
||||
border-radius: 90px;
|
||||
|
||||
&:hover
|
||||
{
|
||||
background-color: rgb(134, 127, 127);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.people
|
||||
{
|
||||
visibility: visible;
|
||||
margin: .25rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
width: 33%;
|
||||
|
||||
@include media-breakpoint-up(sm)
|
||||
{
|
||||
width: 22%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md)
|
||||
{
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg)
|
||||
{
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl)
|
||||
{
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
> div
|
||||
{
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-top: 147.0588%;
|
||||
background-size: cover;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
> p, h6
|
||||
{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
margin-bottom: 0px;
|
||||
|
||||
&.role
|
||||
{
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover
|
||||
{
|
||||
cursor: pointer;
|
||||
|
||||
> img
|
||||
{
|
||||
outline: solid var(--accentColor);
|
||||
}
|
||||
|
||||
.name
|
||||
{
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#leftBtn
|
||||
{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 33%;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#rightBtn
|
||||
{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 33%;
|
||||
outline: none;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||
import { DomSanitizer, Title } from '@angular/platform-browser';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Episode } from "../../models/episode";
|
||||
import { Show } from "../../models/show";
|
||||
@ -19,9 +19,8 @@ export class ShowDetailsComponent implements OnInit
|
||||
|
||||
private toolbar: HTMLElement;
|
||||
private backdrop: HTMLElement;
|
||||
private peopleScroll: HTMLElement;
|
||||
|
||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private http: HttpClient, private snackBar: MatSnackBar, private title: Title)
|
||||
constructor(private route: ActivatedRoute, private http: HttpClient, private snackBar: MatSnackBar, private title: Title)
|
||||
{
|
||||
this.route.queryParams.subscribe(params =>
|
||||
{
|
||||
@ -39,7 +38,6 @@ export class ShowDetailsComponent implements OnInit
|
||||
|
||||
this.toolbar = document.getElementById("toolbar");
|
||||
this.backdrop = document.getElementById("backdrop");
|
||||
this.peopleScroll = document.getElementById("peopleScroll");
|
||||
window.addEventListener("scroll", this.scroll, true);
|
||||
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, 0) !important`);
|
||||
|
||||
@ -78,32 +76,4 @@ export class ShowDetailsComponent implements OnInit
|
||||
this.snackBar.open("An unknow error occured while getting episodes.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
scrollLeft()
|
||||
{
|
||||
let scroll: number = this.peopleScroll.offsetWidth * 0.80;
|
||||
this.peopleScroll.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
|
||||
|
||||
document.getElementById("pl-rightBtn").classList.remove("d-none");
|
||||
|
||||
if (this.peopleScroll.scrollLeft - scroll <= 0)
|
||||
document.getElementById("pl-leftBtn").classList.add("d-none");
|
||||
}
|
||||
|
||||
scrollRight()
|
||||
{
|
||||
let scroll: number = this.peopleScroll.offsetWidth * 0.80;
|
||||
console.log("Scroll: " + scroll);
|
||||
this.peopleScroll.scrollBy({ top: 0, left: scroll, behavior: "smooth" });
|
||||
document.getElementById("pl-leftBtn").classList.remove("d-none");
|
||||
|
||||
if (this.peopleScroll.scrollLeft + scroll >= this.peopleScroll.scrollWidth - this.peopleScroll.clientWidth)
|
||||
document.getElementById("pl-rightBtn").classList.add("d-none");
|
||||
}
|
||||
|
||||
getPeopleIcon(slug: string)
|
||||
{
|
||||
return this.sanitizer.bypassSecurityTrustStyle("url(/peopleimg/" + slug + ")");
|
||||
}
|
||||
}
|
||||
|
12
Kyoo/ClientApp/src/app/shows-list/shows-list.component.html
Normal file
12
Kyoo/ClientApp/src/app/shows-list/shows-list.component.html
Normal file
@ -0,0 +1,12 @@
|
||||
<div class="scroll-row mb-5">
|
||||
<div class="shows-container">
|
||||
<a class="show" *ngFor="let show of this.shows" routerLink="/show/{{show.slug}}" href="/show/{{show.slug}}">
|
||||
<div matRipple [style.background-image]="getThumb(show.slug)"> </div>
|
||||
<p class="title">{{show.title}}</p>
|
||||
<p class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</p>
|
||||
<ng-template #elseBlock><p class="date">{{show.startYear}}</p></ng-template>
|
||||
</a>
|
||||
</div>
|
||||
<button mat-raised-button color="accent" class="scrollBtn d-none" (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
|
||||
<button mat-raised-button color="accent" class="scrollBtn" (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
|
||||
</div>
|
235
Kyoo/ClientApp/src/app/shows-list/shows-list.component.scss
Normal file
235
Kyoo/ClientApp/src/app/shows-list/shows-list.component.scss
Normal file
@ -0,0 +1,235 @@
|
||||
@import "~bootstrap/scss/functions";
|
||||
@import "~bootstrap/scss/variables";
|
||||
@import "~bootstrap/scss/mixins/breakpoints";
|
||||
|
||||
.shows-container
|
||||
{
|
||||
display: flex;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
overflow-x: auto;
|
||||
min-width: 100%;
|
||||
flex-shrink: 0;
|
||||
flex-direction: row;
|
||||
|
||||
&::-webkit-scrollbar
|
||||
{
|
||||
height: 4px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #999;
|
||||
border-radius: 90px;
|
||||
|
||||
&:hover
|
||||
{
|
||||
background-color: rgb(134, 127, 127);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show
|
||||
{
|
||||
width: 33%;
|
||||
min-width: 120px;
|
||||
max-width: 200px;
|
||||
list-style: none;
|
||||
padding: .5em;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
@include media-breakpoint-up(sm)
|
||||
{
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md)
|
||||
{
|
||||
width: 20%;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg)
|
||||
{
|
||||
width: 18%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl)
|
||||
{
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
|
||||
&:focus, &:hover
|
||||
{
|
||||
> div
|
||||
{
|
||||
outline: solid var(--accentColor);
|
||||
}
|
||||
|
||||
> .title
|
||||
{
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
> div
|
||||
{
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-top: 147.0588%;
|
||||
background-size: cover;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
> p
|
||||
{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
margin-bottom: 0px;
|
||||
opacity: 1;
|
||||
|
||||
&.date
|
||||
{
|
||||
opacity: 0.8;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover
|
||||
{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*.people
|
||||
{
|
||||
visibility: visible;
|
||||
margin: .25rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
width: 33%;
|
||||
|
||||
@include media-breakpoint-up(sm)
|
||||
{
|
||||
width: 22%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md)
|
||||
{
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg)
|
||||
{
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl)
|
||||
{
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
> div
|
||||
{
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-top: 147.0588%;
|
||||
background-size: cover;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
> p, h6
|
||||
{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
margin-bottom: 0px;
|
||||
|
||||
&.role
|
||||
{
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover
|
||||
{
|
||||
cursor: pointer;
|
||||
|
||||
> img
|
||||
{
|
||||
outline: solid var(--accentColor);
|
||||
}
|
||||
|
||||
.name
|
||||
{
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
.scroll-row
|
||||
{
|
||||
position: relative;
|
||||
|
||||
&:hover
|
||||
{
|
||||
.scrollBtn
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scrollBtn
|
||||
{
|
||||
padding: 0;
|
||||
outline: none;
|
||||
min-width: 0;
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
bottom: 40%;
|
||||
display: none;
|
||||
|
||||
&#pl-leftBtn
|
||||
{
|
||||
left: 0;
|
||||
padding-left: 10px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
&#pl-rightBtn
|
||||
{
|
||||
right: 0;
|
||||
padding-right: 10px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
#leftBtn
|
||||
{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 33%;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#rightBtn
|
||||
{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 33%;
|
||||
outline: none;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ShowsListComponent } from './shows-list.component';
|
||||
|
||||
describe('ShowsListComponent', () => {
|
||||
let component: ShowsListComponent;
|
||||
let fixture: ComponentFixture<ShowsListComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ShowsListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShowsListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
47
Kyoo/ClientApp/src/app/shows-list/shows-list.component.ts
Normal file
47
Kyoo/ClientApp/src/app/shows-list/shows-list.component.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { Show } from "../../models/show";
|
||||
|
||||
@Component({
|
||||
selector: 'app-shows-list',
|
||||
templateUrl: './shows-list.component.html',
|
||||
styleUrls: ['./shows-list.component.scss']
|
||||
})
|
||||
export class ShowsListComponent implements OnInit
|
||||
{
|
||||
@Input() shows: Show[];
|
||||
private showsScroll: HTMLElement;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) { }
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.showsScroll = document.getElementById("showsScroll");
|
||||
}
|
||||
|
||||
scrollLeft()
|
||||
{
|
||||
let scroll: number = this.showsScroll.offsetWidth * 0.80;
|
||||
this.showsScroll.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
|
||||
|
||||
document.getElementById("pl-rightBtn").classList.remove("d-none");
|
||||
|
||||
if (this.showsScroll.scrollLeft - scroll <= 0)
|
||||
document.getElementById("pl-leftBtn").classList.add("d-none");
|
||||
}
|
||||
|
||||
scrollRight()
|
||||
{
|
||||
let scroll: number = this.showsScroll.offsetWidth * 0.80;
|
||||
this.showsScroll.scrollBy({ top: 0, left: scroll, behavior: "smooth" });
|
||||
document.getElementById("pl-leftBtn").classList.remove("d-none");
|
||||
|
||||
if (this.showsScroll.scrollLeft + scroll >= this.showsScroll.scrollWidth - this.showsScroll.clientWidth)
|
||||
document.getElementById("pl-rightBtn").classList.add("d-none");
|
||||
}
|
||||
|
||||
getThumb(slug: string)
|
||||
{
|
||||
return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")");
|
||||
}
|
||||
}
|
@ -8,5 +8,6 @@ export interface Episode
|
||||
overview: string;
|
||||
releaseDate;
|
||||
runtime: number;
|
||||
externalIDs: string;
|
||||
externalIDs: string;
|
||||
showTitle: string;
|
||||
}
|
||||
|
@ -9,6 +9,6 @@ export interface SearchResut
|
||||
shows: Show[];
|
||||
episodes: Episode[];
|
||||
people: People[];
|
||||
genres: Genre[];
|
||||
genrwes: Genre[];
|
||||
studios: Studio[];
|
||||
}
|
||||
|
@ -682,14 +682,14 @@ namespace Kyoo.InternalAPI
|
||||
{
|
||||
List<Episode> episodes = new List<Episode>();
|
||||
SQLiteDataReader reader;
|
||||
string query = "SELECT * FROM episodes WHERE title LIKE $query ORDER BY seasonNumber, episodeNumber;";
|
||||
string query = "SELECT episodes.*, shows.slug, shows.title as showTitle FROM episodes JOIN shows ON showID = shows.id WHERE episodes.title LIKE $query ORDER BY seasonNumber, episodeNumber LIMIT 20;";
|
||||
|
||||
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("$query", "%" + searchQuery + "%");
|
||||
reader = cmd.ExecuteReader();
|
||||
while (reader.Read())
|
||||
episodes.Add(Episode.FromReader(reader));
|
||||
episodes.Add(Episode.FromReader(reader).SetThumb(reader["slug"] as string).SetShowTitle(reader["showTitle"] as string));
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
@ -698,7 +698,7 @@ namespace Kyoo.InternalAPI
|
||||
{
|
||||
List<People> people = new List<People>();
|
||||
SQLiteDataReader reader;
|
||||
string query = "SELECT * FROM people WHERE name LIKE $query ORDER BY name;";
|
||||
string query = "SELECT * FROM people WHERE name LIKE $query ORDER BY name LIMIT 40;";
|
||||
|
||||
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
||||
{
|
||||
|
@ -22,7 +22,8 @@ namespace Kyoo.Models
|
||||
[JsonIgnore] public string ImgPrimary;
|
||||
public string ExternalIDs;
|
||||
|
||||
public string Link; //Used only on the player
|
||||
public string ShowTitle; //Used in the API response only
|
||||
public string Link; //Used in the API response only
|
||||
public string Thumb; //Used in the API response only
|
||||
|
||||
|
||||
@ -86,6 +87,12 @@ namespace Kyoo.Models
|
||||
return this;
|
||||
}
|
||||
|
||||
public Episode SetShowTitle(string showTite)
|
||||
{
|
||||
ShowTitle = showTite;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static string GetSlug(string showSlug, long seasonNumber, long episodeNumber)
|
||||
{
|
||||
return showSlug + "-s" + seasonNumber + "e" + episodeNumber;
|
||||
|
Loading…
x
Reference in New Issue
Block a user