refactor: reorganize component structure and update imports
This commit is contained in:
parent
9c14319236
commit
d77aee2b9f
35 changed files with 698 additions and 689 deletions
|
|
@ -1,17 +1,17 @@
|
||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import { HomeComponent } from './components/home/home.component';
|
|
||||||
import { ImprintComponent } from './shared/components/legal-information/imprint/imprint.component';
|
import { ImprintComponent } from './shared/components/legal-information/imprint/imprint.component';
|
||||||
import { PrivacyPolicyComponent } from './shared/components/legal-information/privacy-policy/privacy-policy.component';
|
import { PrivacyPolicyComponent } from './shared/components/legal-information/privacy-policy/privacy-policy.component';
|
||||||
import { BrowseComponent } from './components/home/browse/browse.component';
|
import { HomeComponent } from './components/home/home.component';
|
||||||
|
import { AuthComponent } from './components/auth/auth.component';
|
||||||
import { AuthGuard } from './auth.guard';
|
import { AuthGuard } from './auth.guard';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', component: HomeComponent },
|
{ path: '', component: AuthComponent },
|
||||||
{ path: 'login', component: HomeComponent },
|
{ path: 'login', component: AuthComponent },
|
||||||
{ path: 'register', component: HomeComponent },
|
{ path: 'register', component: AuthComponent },
|
||||||
{ path: 'forgot-password', component: HomeComponent },
|
{ path: 'forgot-password', component: AuthComponent },
|
||||||
{ path: 'verify-email', component: HomeComponent },
|
{ path: 'verify-email', component: AuthComponent },
|
||||||
{ path: 'imprint', component: ImprintComponent },
|
{ path: 'imprint', component: ImprintComponent },
|
||||||
{ path: 'privacy-policy', component: PrivacyPolicyComponent },
|
{ path: 'privacy-policy', component: PrivacyPolicyComponent },
|
||||||
{ path: 'browse', component: BrowseComponent, canActivate: [AuthGuard] },
|
{ path: 'browse', component: HomeComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,22 @@
|
||||||
<section>
|
<section class="bg-home">
|
||||||
<div class="headline">
|
<app-header />
|
||||||
<p>Movies, TV shows, and more</p>
|
|
||||||
<p>Watch whenever you want wherever you want</p>
|
<!-- Non auth Routing -->
|
||||||
<p>Enter your email to create or restart your subscription.</p>
|
@switch (currentRoute) { @case ('') {
|
||||||
</div>
|
<app-landing-page />
|
||||||
<form
|
} @case ('login') {
|
||||||
#taskForm="ngForm"
|
<app-login />
|
||||||
(ngSubmit)="onSubmit(taskForm, mail)"
|
} @case ('register') {
|
||||||
onsubmit="return false"
|
<app-register />
|
||||||
>
|
} @case ('forgot-password') {
|
||||||
<input
|
<app-forgot-password />
|
||||||
type="mail"
|
} @case ('verify-email') {
|
||||||
id="mail"
|
<app-verify-email />
|
||||||
name="mail"
|
} }
|
||||||
#mail="ngModel"
|
|
||||||
placeholder="Email Address"
|
<app-footer />
|
||||||
class="custom-input"
|
|
||||||
autocomplete="email"
|
@if (errorService.displayError) {
|
||||||
[(ngModel)]="authData.mail"
|
<app-error-toast />
|
||||||
[class.error-border]="
|
}
|
||||||
(!mail.valid && mail.touched) ||
|
|
||||||
(mail.touched &&
|
|
||||||
!isUserEmailValid(authData.mail.toLowerCase()) &&
|
|
||||||
authData.mail.length > 0)
|
|
||||||
"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<div class="error-msg">
|
|
||||||
@if (!mail.valid && mail.touched) {
|
|
||||||
<p>Please enter your email</p>
|
|
||||||
} @else { @if (mail.touched && authData.mail.length > 0 &&
|
|
||||||
!isUserEmailValid(authData.mail.toLowerCase())) {
|
|
||||||
<p>This is not a valid email format</p>
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
<app-btn-large
|
|
||||||
[value]="'Sign Up'"
|
|
||||||
[imgPath]="'play'"
|
|
||||||
[imgPath]="'arrow_right'"
|
|
||||||
[imgReverse]="true"
|
|
||||||
[disabled]="
|
|
||||||
(!isUserEmailValid(authData.mail) && authData.mail.length > 0) ||
|
|
||||||
authData.send
|
|
||||||
"
|
|
||||||
></app-btn-large>
|
|
||||||
</form>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,88 +1,4 @@
|
||||||
@use "./../../../assets/style/colors.scss" as *;
|
|
||||||
@use "./../../../assets/style/form.scss" as *;
|
|
||||||
@use "./../../../assets/style/auth-layout.scss" as *;
|
|
||||||
|
|
||||||
section {
|
section {
|
||||||
background-image: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
|
width: 100dvw;
|
||||||
url(./../../../assets/img/backgrounds/home.png);
|
height: 100dvh;
|
||||||
}
|
|
||||||
|
|
||||||
.headline {
|
|
||||||
p:nth-child(1) {
|
|
||||||
font-size: 48px;
|
|
||||||
font-weight: 700;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
p:nth-child(2) {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
p:nth-child(3) {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
flex-direction: row;
|
|
||||||
position: relative;
|
|
||||||
width: 500px;
|
|
||||||
padding: 24px;
|
|
||||||
input {
|
|
||||||
width: 65%;
|
|
||||||
padding: 12px 18px;
|
|
||||||
margin: 8px 0;
|
|
||||||
color: $white;
|
|
||||||
border: 2px solid $white;
|
|
||||||
border-radius: 24px;
|
|
||||||
font-size: 18px;
|
|
||||||
&::placeholder {
|
|
||||||
color: rgba($white, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.error-border {
|
|
||||||
border: 2px solid $red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-msg {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
padding: 6px;
|
|
||||||
width: 57%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*------------- RESPONSIVE -------------*/
|
|
||||||
|
|
||||||
@media screen and (max-width: 550px) {
|
|
||||||
form {
|
|
||||||
flex-direction: column;
|
|
||||||
width: 90%;
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-msg {
|
|
||||||
position: relative;
|
|
||||||
padding: 0;
|
|
||||||
height: 12px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 500px) {
|
|
||||||
.headline {
|
|
||||||
p:nth-child(1) {
|
|
||||||
font-size: 36px;
|
|
||||||
}
|
|
||||||
p:nth-child(2) {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
p:nth-child(3) {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,44 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormsModule, NgForm } from '@angular/forms';
|
import { HeaderComponent } from '../../shared/components/header/header.component';
|
||||||
import { BtnLargeComponent } from '../../shared/components/buttons/btn-large/btn-large.component';
|
import { FooterComponent } from '../../shared/components/footer/footer.component';
|
||||||
import { Router } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { RegisterComponent } from '../auth/register/register.component';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { LoginComponent } from '../auth/login/login.component';
|
||||||
|
import { ForgotPasswordComponent } from '../auth/forgot-password/forgot-password.component';
|
||||||
|
import { VerifyEmailComponent } from '../auth/verify-email/verify-email.component';
|
||||||
|
import { ErrorToastComponent } from '../../shared/components/error-toast/error-toast.component';
|
||||||
import { ErrorService } from '../../services/error.service';
|
import { ErrorService } from '../../services/error.service';
|
||||||
import { AuthService } from '../../services/auth.service';
|
import { LandingPageComponent } from './landing-page/landing-page.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-auth',
|
selector: 'app-auth',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, BtnLargeComponent, FormsModule],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
HeaderComponent,
|
||||||
|
FooterComponent,
|
||||||
|
LandingPageComponent,
|
||||||
|
RegisterComponent,
|
||||||
|
LoginComponent,
|
||||||
|
ForgotPasswordComponent,
|
||||||
|
VerifyEmailComponent,
|
||||||
|
ErrorToastComponent,
|
||||||
|
],
|
||||||
templateUrl: './auth.component.html',
|
templateUrl: './auth.component.html',
|
||||||
styleUrl: './auth.component.scss',
|
styleUrl: './auth.component.scss',
|
||||||
})
|
})
|
||||||
export class AuthComponent {
|
export class AuthComponent implements OnInit {
|
||||||
authData = {
|
currentRoute: any;
|
||||||
mail: '',
|
|
||||||
send: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private route: ActivatedRoute,
|
||||||
public errorService: ErrorService,
|
public errorService: ErrorService
|
||||||
private authService: AuthService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
isUserEmailValid(emailValue: string) {
|
ngOnInit(): void {
|
||||||
const emailRegex = /^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}$/;
|
this.route.url.subscribe((url) => {
|
||||||
return emailRegex.test(emailValue);
|
this.currentRoute = url[0]?.path || '';
|
||||||
}
|
});
|
||||||
|
|
||||||
async onSubmit(ngForm: NgForm, mailInput: any) {
|
|
||||||
if (ngForm.submitted && ngForm.form.valid) {
|
|
||||||
await this.checkDuplicatesEmail();
|
|
||||||
} else {
|
|
||||||
mailInput.control.markAsTouched();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkDuplicatesEmail() {
|
|
||||||
const body = {
|
|
||||||
email: this.authData.mail,
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
this.authData.send = true;
|
|
||||||
await this.authService.checkAuthUserMail(body);
|
|
||||||
const queryParams = { mail: this.authData.mail };
|
|
||||||
this.router.navigate(['/register'], { queryParams });
|
|
||||||
this.errorService.clearError();
|
|
||||||
} catch (error) {
|
|
||||||
this.authData.send = false;
|
|
||||||
this.errorService.handleError(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<section>
|
||||||
|
<div class="headline">
|
||||||
|
<p>Movies, TV shows, and more</p>
|
||||||
|
<p>Watch whenever you want wherever you want</p>
|
||||||
|
<p>Enter your email to create or restart your subscription.</p>
|
||||||
|
</div>
|
||||||
|
<form
|
||||||
|
#taskForm="ngForm"
|
||||||
|
(ngSubmit)="onSubmit(taskForm, mail)"
|
||||||
|
onsubmit="return false"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="mail"
|
||||||
|
id="mail"
|
||||||
|
name="mail"
|
||||||
|
#mail="ngModel"
|
||||||
|
placeholder="Email Address"
|
||||||
|
class="custom-input"
|
||||||
|
autocomplete="email"
|
||||||
|
[(ngModel)]="authData.mail"
|
||||||
|
[class.error-border]="
|
||||||
|
(!mail.valid && mail.touched) ||
|
||||||
|
(mail.touched &&
|
||||||
|
!isUserEmailValid(authData.mail.toLowerCase()) &&
|
||||||
|
authData.mail.length > 0)
|
||||||
|
"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<div class="error-msg">
|
||||||
|
@if (!mail.valid && mail.touched) {
|
||||||
|
<p>Please enter your email</p>
|
||||||
|
} @else { @if (mail.touched && authData.mail.length > 0 &&
|
||||||
|
!isUserEmailValid(authData.mail.toLowerCase())) {
|
||||||
|
<p>This is not a valid email format</p>
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<app-btn-large
|
||||||
|
[value]="'Sign Up'"
|
||||||
|
[imgPath]="'play'"
|
||||||
|
[imgPath]="'arrow_right'"
|
||||||
|
[imgReverse]="true"
|
||||||
|
[disabled]="
|
||||||
|
(!isUserEmailValid(authData.mail) && authData.mail.length > 0) ||
|
||||||
|
authData.send
|
||||||
|
"
|
||||||
|
></app-btn-large>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
@use "./../../../../assets/style/colors.scss" as *;
|
||||||
|
@use "./../../../../assets/style/form.scss" as *;
|
||||||
|
@use "./../../../../assets/style/auth-layout.scss" as *;
|
||||||
|
|
||||||
|
section {
|
||||||
|
background-image: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
|
||||||
|
url(./../../../../assets/img/backgrounds/home.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline {
|
||||||
|
p:nth-child(1) {
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
p:nth-child(2) {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
p:nth-child(3) {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
flex-direction: row;
|
||||||
|
position: relative;
|
||||||
|
width: 500px;
|
||||||
|
padding: 24px;
|
||||||
|
input {
|
||||||
|
width: 65%;
|
||||||
|
padding: 12px 18px;
|
||||||
|
margin: 8px 0;
|
||||||
|
color: $white;
|
||||||
|
border: 2px solid $white;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-size: 18px;
|
||||||
|
&::placeholder {
|
||||||
|
color: rgba($white, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.error-border {
|
||||||
|
border: 2px solid $red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-msg {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 6px;
|
||||||
|
width: 57%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------- RESPONSIVE -------------*/
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
form {
|
||||||
|
flex-direction: column;
|
||||||
|
width: 90%;
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-msg {
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
height: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
.headline {
|
||||||
|
p:nth-child(1) {
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
p:nth-child(2) {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
p:nth-child(3) {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { BrowseComponent } from './browse.component';
|
import { LandingPageComponent } from './landing-page.component';
|
||||||
|
|
||||||
describe('BrowseComponent', () => {
|
describe('LandingPageComponent', () => {
|
||||||
let component: BrowseComponent;
|
let component: LandingPageComponent;
|
||||||
let fixture: ComponentFixture<BrowseComponent>;
|
let fixture: ComponentFixture<LandingPageComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [BrowseComponent]
|
imports: [LandingPageComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(BrowseComponent);
|
fixture = TestBed.createComponent(LandingPageComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { FormsModule, NgForm } from '@angular/forms';
|
||||||
|
import { BtnLargeComponent } from '../../../shared/components/buttons/btn-large/btn-large.component';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ErrorService } from '../../../services/error.service';
|
||||||
|
import { AuthService } from '../../../services/auth.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-landing-page',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, BtnLargeComponent, FormsModule],
|
||||||
|
templateUrl: './landing-page.component.html',
|
||||||
|
styleUrl: './landing-page.component.scss',
|
||||||
|
})
|
||||||
|
export class LandingPageComponent {
|
||||||
|
authData = {
|
||||||
|
mail: '',
|
||||||
|
send: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
public errorService: ErrorService,
|
||||||
|
private authService: AuthService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
isUserEmailValid(emailValue: string) {
|
||||||
|
const emailRegex = /^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
return emailRegex.test(emailValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSubmit(ngForm: NgForm, mailInput: any) {
|
||||||
|
if (ngForm.submitted && ngForm.form.valid) {
|
||||||
|
await this.checkDuplicatesEmail();
|
||||||
|
} else {
|
||||||
|
mailInput.control.markAsTouched();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkDuplicatesEmail() {
|
||||||
|
const body = {
|
||||||
|
email: this.authData.mail,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
this.authData.send = true;
|
||||||
|
await this.authService.checkAuthUserMail(body);
|
||||||
|
const queryParams = { mail: this.authData.mail };
|
||||||
|
this.router.navigate(['/register'], { queryParams });
|
||||||
|
this.errorService.clearError();
|
||||||
|
} catch (error) {
|
||||||
|
this.authData.send = false;
|
||||||
|
this.errorService.handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
<section>
|
|
||||||
<div *ngIf="isLoading"></div>
|
|
||||||
@if (!isLoading && movies.length > 0) {
|
|
||||||
<!-- If at least one film has been uploaded -->
|
|
||||||
@if (playMovie === "") {
|
|
||||||
<app-header
|
|
||||||
(moviesChange)="onMoviesChange($event)"
|
|
||||||
[showFullLogo]="false"
|
|
||||||
></app-header>
|
|
||||||
<!-- Hero Banner -->
|
|
||||||
<app-hero-banner
|
|
||||||
*ngIf="
|
|
||||||
currentMovie.length === 1 ||
|
|
||||||
(this.isWideScreen() && currentMovie.length > 1)
|
|
||||||
"
|
|
||||||
[isWideScreen]="isWideScreen()"
|
|
||||||
[currentMovie]="currentMovie"
|
|
||||||
[favoriteMovies]="favoriteMovies"
|
|
||||||
[watchedMovies]="watchedMovies"
|
|
||||||
(playMovie)="playVideo($event)"
|
|
||||||
(movieIsUploadedChange)="onMovieIsUploadedChange($event)"
|
|
||||||
(moviesChange)="onMoviesChange($event)"
|
|
||||||
(refreshChange)="onRefreshPage($event)"
|
|
||||||
(favoriteMovieChange)="onFavoriteMovieChange($event)"
|
|
||||||
></app-hero-banner>
|
|
||||||
<!-- Spacer -->
|
|
||||||
<div *ngIf="!this.isWideScreen()" class="spacer"></div>
|
|
||||||
<!-- Movie Categories -->
|
|
||||||
<app-categories
|
|
||||||
*ngIf="
|
|
||||||
this.isWideScreen() || (!this.isWideScreen() && currentMovie.length === 0)
|
|
||||||
"
|
|
||||||
[movies]="movies"
|
|
||||||
[currentMovie]="currentMovie[0]?.id"
|
|
||||||
[favoriteMovies]="favoriteMovies"
|
|
||||||
[watchedMovies]="watchedMovies"
|
|
||||||
(currentMovieId)="currentMovieId($event)"
|
|
||||||
></app-categories>
|
|
||||||
} @else {
|
|
||||||
<div class="video-overlay">
|
|
||||||
<div class="video-header">
|
|
||||||
<div class="back-button" (click)="closeVideo()">
|
|
||||||
<img src="./../../../../assets/img/back.svg" alt="Back" />
|
|
||||||
</div>
|
|
||||||
<div class="center">
|
|
||||||
<div class="resolution-controls">
|
|
||||||
<button
|
|
||||||
[ngClass]="{
|
|
||||||
'resolution-btn': true,
|
|
||||||
active: currentResolution === '360p',
|
|
||||||
'not-available': !movieIsUploaded['360']
|
|
||||||
}"
|
|
||||||
[disabled]="!movieIsUploaded['360'] || currentResolution === '360p'"
|
|
||||||
(click)="changeResolution('360p')"
|
|
||||||
>
|
|
||||||
360p
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
[ngClass]="{
|
|
||||||
'resolution-btn': true,
|
|
||||||
active: currentResolution === '720p',
|
|
||||||
'not-available': !movieIsUploaded['720']
|
|
||||||
}"
|
|
||||||
[disabled]="!movieIsUploaded['720'] || currentResolution === '720p'"
|
|
||||||
(click)="changeResolution('720p')"
|
|
||||||
>
|
|
||||||
720p
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
[ngClass]="{
|
|
||||||
'resolution-btn': true,
|
|
||||||
active: currentResolution === '1080p',
|
|
||||||
'not-available': !movieIsUploaded['1080']
|
|
||||||
}"
|
|
||||||
[disabled]="
|
|
||||||
!movieIsUploaded['1080'] || currentResolution === '1080p'
|
|
||||||
"
|
|
||||||
(click)="changeResolution('1080p')"
|
|
||||||
>
|
|
||||||
1080p
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p
|
|
||||||
*ngIf="
|
|
||||||
!movieIsUploaded['360'] ||
|
|
||||||
!movieIsUploaded['720'] ||
|
|
||||||
!movieIsUploaded['1080']
|
|
||||||
"
|
|
||||||
>
|
|
||||||
(If a button cannot be selected, the video will be converted, which
|
|
||||||
may take a few seconds.)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="logo">
|
|
||||||
<img src="./../../../../assets/img/logo_ci.svg" alt="Logo" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<app-video-player [playMovie]="playMovie"></app-video-player>
|
|
||||||
</div>
|
|
||||||
} } @if (!isLoading && movies.length === 0) {
|
|
||||||
<!-- If the database is empty -->
|
|
||||||
<app-header [showFullLogo]="false"></app-header>
|
|
||||||
<div class="error">
|
|
||||||
<h2>Unfortunately there are no films available in the database.</h2>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div class="add-button">
|
|
||||||
<app-btn-small
|
|
||||||
[imgPath]="'add'"
|
|
||||||
(click)="toggleUploadMovieOverview(true)"
|
|
||||||
></app-btn-small>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@if (uploadMovieOverview) {
|
|
||||||
<app-upload-movie
|
|
||||||
(toggleUploadMovieOverview)="toggleUploadMovieOverview($event)"
|
|
||||||
></app-upload-movie>
|
|
||||||
}
|
|
||||||
|
|
@ -1,183 +0,0 @@
|
||||||
@use "./../../../../assets/style/colors.scss" as *;
|
|
||||||
|
|
||||||
section {
|
|
||||||
height: 100dvh;
|
|
||||||
width: 100dvw;
|
|
||||||
background-color: $black;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: $black;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 1000;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
video {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
&:hover .video-header {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-header {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 24px 48px;
|
|
||||||
background: rgba($black, 0.5);
|
|
||||||
z-index: 1001;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 300ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
p {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: $light-blue;
|
|
||||||
text-shadow: 1px 1px 2px $black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacer {
|
|
||||||
margin-top: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-button {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 24px;
|
|
||||||
right: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
cursor: pointer;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
img {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
filter: brightness(0) saturate(100%) invert(100%) sepia(6%) saturate(312%)
|
|
||||||
hue-rotate(234deg) brightness(113%) contrast(100%);
|
|
||||||
transition: filter 300ms ease-in-out;
|
|
||||||
&:hover {
|
|
||||||
filter: brightness(0) saturate(100%) invert(13%) sepia(90%)
|
|
||||||
saturate(7329%) hue-rotate(344deg) brightness(105%) contrast(117%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo img {
|
|
||||||
width: 32px;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
position: fixed;
|
|
||||||
top: 120px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 24px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolution Buttons
|
|
||||||
|
|
||||||
.resolution-controls {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 12px;
|
|
||||||
margin: 18px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resolution-btn {
|
|
||||||
background-color: $light-blue;
|
|
||||||
color: $white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.3s ease, transform 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resolution-btn.active {
|
|
||||||
background-color: $blue;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resolution-btn:disabled {
|
|
||||||
cursor: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resolution-btn:not(.active):hover {
|
|
||||||
background-color: $blue;
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.resolution-btn:focus {
|
|
||||||
outline: none;
|
|
||||||
box-shadow: 0 0 0 3px rgba(38, 143, 255, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.not-available {
|
|
||||||
opacity: 0.5;
|
|
||||||
background-color: $gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.not-available:not(.active):hover {
|
|
||||||
background-color: $gray;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*------------- RESPONSIVE -------------*/
|
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
|
||||||
.video-header {
|
|
||||||
padding: 12px 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 550px) {
|
|
||||||
form {
|
|
||||||
flex-direction: column;
|
|
||||||
width: 90%;
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-msg {
|
|
||||||
position: relative;
|
|
||||||
padding: 0;
|
|
||||||
height: 12px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 500px) {
|
|
||||||
.resolution-btn {
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 3px 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,142 +0,0 @@
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
|
||||||
import { HeaderComponent } from '../../../shared/components/header/header.component';
|
|
||||||
import { HeroBannerComponent } from './hero-banner/hero-banner.component';
|
|
||||||
import { CategoriesComponent } from './categories/categories.component';
|
|
||||||
import { MovieService } from '../../../services/movie.service';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { VideoPlayerComponent } from './video-player/video-player.component';
|
|
||||||
import { BtnSmallComponent } from '../../../shared/components/buttons/btn-small/btn-small.component';
|
|
||||||
import { UploadMovieComponent } from './upload-movie/upload-movie.component';
|
|
||||||
import { UserService } from '../../../services/user.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-browse',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
HeaderComponent,
|
|
||||||
HeroBannerComponent,
|
|
||||||
CategoriesComponent,
|
|
||||||
VideoPlayerComponent,
|
|
||||||
BtnSmallComponent,
|
|
||||||
UploadMovieComponent,
|
|
||||||
],
|
|
||||||
templateUrl: './browse.component.html',
|
|
||||||
styleUrl: './browse.component.scss',
|
|
||||||
})
|
|
||||||
export class BrowseComponent implements OnInit {
|
|
||||||
@ViewChild(VideoPlayerComponent) videoPlayer!: VideoPlayerComponent;
|
|
||||||
movies: any[] = [];
|
|
||||||
favoriteMovies: number[] = [];
|
|
||||||
watchedMovies: number[] = [];
|
|
||||||
currentMovie: any[] = [];
|
|
||||||
playMovie: string = '';
|
|
||||||
isLoading: boolean = true;
|
|
||||||
uploadMovieOverview: boolean = false;
|
|
||||||
currentResolution: '360p' | '720p' | '1080p' = '720p';
|
|
||||||
movieIsUploaded: { [resolution: string]: boolean } = {
|
|
||||||
'360': false,
|
|
||||||
'720': false,
|
|
||||||
'1080': false,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private movieService: MovieService,
|
|
||||||
public userService: UserService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this.loadLikedAndWatchedMovies();
|
|
||||||
await this.loadAllMovies();
|
|
||||||
if (this.isWideScreen()) {
|
|
||||||
this.currentMovie.length === 0 ? this.loadRandomMovie() : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadLikedAndWatchedMovies() {
|
|
||||||
try {
|
|
||||||
const userData = await this.userService.getLikedAndWatchedMovies();
|
|
||||||
this.favoriteMovies = userData.liked_videos;
|
|
||||||
this.watchedMovies = userData.watched_videos;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateLikeMovies() {
|
|
||||||
const body = {
|
|
||||||
liked_videos: this.favoriteMovies,
|
|
||||||
};
|
|
||||||
this.userService.updateLikedMovies(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
onRefreshPage(updatedMovies: any[]) {
|
|
||||||
this.currentMovie = [];
|
|
||||||
setTimeout(() => {
|
|
||||||
this.currentMovie = updatedMovies;
|
|
||||||
}, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMoviesChange(updatedMovies: any[]) {
|
|
||||||
if (this.isWideScreen()) {
|
|
||||||
this.loadRandomMovie();
|
|
||||||
} else {
|
|
||||||
this.currentMovie = updatedMovies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isWideScreen() {
|
|
||||||
return window.innerWidth > 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMovieIsUploadedChange(newStatus: { [resolution: string]: boolean }) {
|
|
||||||
this.movieIsUploaded = newStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
onFavoriteMovieChange(favoriteMovies: any) {
|
|
||||||
this.favoriteMovies = favoriteMovies;
|
|
||||||
this.updateLikeMovies();
|
|
||||||
}
|
|
||||||
|
|
||||||
changeResolution(resolution: '360p' | '720p' | '1080p') {
|
|
||||||
if (this.videoPlayer && this.movieIsUploaded[resolution.replace('p', '')]) {
|
|
||||||
this.videoPlayer.switchResolution(resolution);
|
|
||||||
this.currentResolution = resolution;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadAllMovies() {
|
|
||||||
this.isLoading = true;
|
|
||||||
try {
|
|
||||||
this.movies = await this.movieService.getAllMovies();
|
|
||||||
} finally {
|
|
||||||
this.isLoading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closeVideo(): void {
|
|
||||||
this.playMovie = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
playVideo(videoPath: string) {
|
|
||||||
this.currentResolution = '720p';
|
|
||||||
this.playMovie = videoPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadRandomMovie(): void {
|
|
||||||
const randomIndex = Math.floor(Math.random() * this.movies.length);
|
|
||||||
this.currentMovie = [this.movies[randomIndex]];
|
|
||||||
}
|
|
||||||
|
|
||||||
currentMovieId(movieId: number) {
|
|
||||||
let index = this.movies.findIndex((movie) => movie.id === movieId);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.currentMovie = [];
|
|
||||||
this.currentMovie.push(this.movies[index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleUploadMovieOverview(value: any) {
|
|
||||||
this.uploadMovieOverview = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@use "./../../../../../assets/style/colors.scss" as *;
|
@use "./../../../../assets/style/colors.scss" as *;
|
||||||
|
|
||||||
section {
|
section {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
|
@ -7,7 +7,7 @@ import {
|
||||||
Input,
|
Input,
|
||||||
Output,
|
Output,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { environment } from '../../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
import { MoviesListComponent } from './movie-list/movie-list.component';
|
import { MoviesListComponent } from './movie-list/movie-list.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@use "./../../../../../../assets/style/colors.scss" as *;
|
@use "./../../../../../assets/style/colors.scss" as *;
|
||||||
|
|
||||||
.movies {
|
.movies {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||||
import { environment } from '../../../../../../environments/environment';
|
import { environment } from '../../../../../environments/environment';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@use "./../../../../../assets/style/colors.scss" as *;
|
@use "./../../../../assets/style/colors.scss" as *;
|
||||||
|
|
||||||
section {
|
section {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
|
|
@ -9,11 +9,11 @@ import {
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { BtnLargeComponent } from '../../../../shared/components/buttons/btn-large/btn-large.component';
|
import { BtnLargeComponent } from '../../../shared/components/buttons/btn-large/btn-large.component';
|
||||||
import { MovieService } from '../../../../services/movie.service';
|
import { MovieService } from '../../../services/movie.service';
|
||||||
import { environment } from '../../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
import { BtnSmallComponent } from '../../../../shared/components/buttons/btn-small/btn-small.component';
|
import { BtnSmallComponent } from '../../../shared/components/buttons/btn-small/btn-small.component';
|
||||||
import { UserService } from '../../../../services/user.service';
|
import { UserService } from '../../../services/user.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-banner',
|
selector: 'app-hero-banner',
|
||||||
|
|
@ -1,12 +1,118 @@
|
||||||
<section class="bg-home">
|
<section>
|
||||||
<app-header></app-header>
|
<div *ngIf="isLoading"></div>
|
||||||
<app-auth *ngIf="currentRoute === ''"></app-auth>
|
@if (!isLoading && movies.length > 0) {
|
||||||
<app-login *ngIf="currentRoute === 'login'"></app-login>
|
<!-- If at least one film has been uploaded -->
|
||||||
<app-register *ngIf="currentRoute === 'register'"></app-register>
|
@if (playMovie === "") {
|
||||||
<app-forgot-password
|
<app-header
|
||||||
*ngIf="currentRoute === 'forgot-password'"
|
(moviesChange)="onMoviesChange($event)"
|
||||||
></app-forgot-password>
|
[showFullLogo]="false"
|
||||||
<app-verify-email *ngIf="currentRoute === 'verify-email'"></app-verify-email>
|
></app-header>
|
||||||
<app-footer></app-footer>
|
<!-- Hero Banner -->
|
||||||
<app-error-toast *ngIf="errorService.displayError"></app-error-toast>
|
<app-hero-banner
|
||||||
|
*ngIf="
|
||||||
|
currentMovie.length === 1 ||
|
||||||
|
(this.isWideScreen() && currentMovie.length > 1)
|
||||||
|
"
|
||||||
|
[isWideScreen]="isWideScreen()"
|
||||||
|
[currentMovie]="currentMovie"
|
||||||
|
[favoriteMovies]="favoriteMovies"
|
||||||
|
[watchedMovies]="watchedMovies"
|
||||||
|
(playMovie)="playVideo($event)"
|
||||||
|
(movieIsUploadedChange)="onMovieIsUploadedChange($event)"
|
||||||
|
(moviesChange)="onMoviesChange($event)"
|
||||||
|
(refreshChange)="onRefreshPage($event)"
|
||||||
|
(favoriteMovieChange)="onFavoriteMovieChange($event)"
|
||||||
|
></app-hero-banner>
|
||||||
|
<!-- Spacer -->
|
||||||
|
<div *ngIf="!this.isWideScreen()" class="spacer"></div>
|
||||||
|
<!-- Movie Categories -->
|
||||||
|
<app-categories
|
||||||
|
*ngIf="
|
||||||
|
this.isWideScreen() || (!this.isWideScreen() && currentMovie.length === 0)
|
||||||
|
"
|
||||||
|
[movies]="movies"
|
||||||
|
[currentMovie]="currentMovie[0]?.id"
|
||||||
|
[favoriteMovies]="favoriteMovies"
|
||||||
|
[watchedMovies]="watchedMovies"
|
||||||
|
(currentMovieId)="currentMovieId($event)"
|
||||||
|
></app-categories>
|
||||||
|
} @else {
|
||||||
|
<div class="video-overlay">
|
||||||
|
<div class="video-header">
|
||||||
|
<div class="back-button" (click)="closeVideo()">
|
||||||
|
<img src="./../../../../assets/img/back.svg" alt="Back" />
|
||||||
|
</div>
|
||||||
|
<div class="center">
|
||||||
|
<div class="resolution-controls">
|
||||||
|
<button
|
||||||
|
[ngClass]="{
|
||||||
|
'resolution-btn': true,
|
||||||
|
active: currentResolution === '360p',
|
||||||
|
'not-available': !movieIsUploaded['360']
|
||||||
|
}"
|
||||||
|
[disabled]="!movieIsUploaded['360'] || currentResolution === '360p'"
|
||||||
|
(click)="changeResolution('360p')"
|
||||||
|
>
|
||||||
|
360p
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
[ngClass]="{
|
||||||
|
'resolution-btn': true,
|
||||||
|
active: currentResolution === '720p',
|
||||||
|
'not-available': !movieIsUploaded['720']
|
||||||
|
}"
|
||||||
|
[disabled]="!movieIsUploaded['720'] || currentResolution === '720p'"
|
||||||
|
(click)="changeResolution('720p')"
|
||||||
|
>
|
||||||
|
720p
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
[ngClass]="{
|
||||||
|
'resolution-btn': true,
|
||||||
|
active: currentResolution === '1080p',
|
||||||
|
'not-available': !movieIsUploaded['1080']
|
||||||
|
}"
|
||||||
|
[disabled]="
|
||||||
|
!movieIsUploaded['1080'] || currentResolution === '1080p'
|
||||||
|
"
|
||||||
|
(click)="changeResolution('1080p')"
|
||||||
|
>
|
||||||
|
1080p
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
*ngIf="
|
||||||
|
!movieIsUploaded['360'] ||
|
||||||
|
!movieIsUploaded['720'] ||
|
||||||
|
!movieIsUploaded['1080']
|
||||||
|
"
|
||||||
|
>
|
||||||
|
(If a button cannot be selected, the video will be converted, which
|
||||||
|
may take a few seconds.)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="logo">
|
||||||
|
<img src="./../../../../assets/img/logo_ci.svg" alt="Logo" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<app-video-player [playMovie]="playMovie"></app-video-player>
|
||||||
|
</div>
|
||||||
|
} } @if (!isLoading && movies.length === 0) {
|
||||||
|
<!-- If the database is empty -->
|
||||||
|
<app-header [showFullLogo]="false"></app-header>
|
||||||
|
<div class="error">
|
||||||
|
<h2>Unfortunately there are no films available in the database.</h2>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="add-button">
|
||||||
|
<app-btn-small
|
||||||
|
[imgPath]="'add'"
|
||||||
|
(click)="toggleUploadMovieOverview(true)"
|
||||||
|
></app-btn-small>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@if (uploadMovieOverview) {
|
||||||
|
<app-upload-movie
|
||||||
|
(toggleUploadMovieOverview)="toggleUploadMovieOverview($event)"
|
||||||
|
></app-upload-movie>
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,183 @@
|
||||||
|
@use "./../../../assets/style/colors.scss" as *;
|
||||||
|
|
||||||
section {
|
section {
|
||||||
width: 100dvw;
|
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
|
width: 100dvw;
|
||||||
|
background-color: $black;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: $black;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
&:hover .video-header {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-header {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24px 48px;
|
||||||
|
background: rgba($black, 0.5);
|
||||||
|
z-index: 1001;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 300ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
p {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: $light-blue;
|
||||||
|
text-shadow: 1px 1px 2px $black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
margin-top: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-button {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-button {
|
||||||
|
cursor: pointer;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(100%) sepia(6%) saturate(312%)
|
||||||
|
hue-rotate(234deg) brightness(113%) contrast(100%);
|
||||||
|
transition: filter 300ms ease-in-out;
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(0) saturate(100%) invert(13%) sepia(90%)
|
||||||
|
saturate(7329%) hue-rotate(344deg) brightness(105%) contrast(117%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo img {
|
||||||
|
width: 32px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 120px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolution Buttons
|
||||||
|
|
||||||
|
.resolution-controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin: 18px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resolution-btn {
|
||||||
|
background-color: $light-blue;
|
||||||
|
color: $white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resolution-btn.active {
|
||||||
|
background-color: $blue;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resolution-btn:disabled {
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resolution-btn:not(.active):hover {
|
||||||
|
background-color: $blue;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resolution-btn:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 3px rgba(38, 143, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-available {
|
||||||
|
opacity: 0.5;
|
||||||
|
background-color: $gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-available:not(.active):hover {
|
||||||
|
background-color: $gray;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------- RESPONSIVE -------------*/
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
.video-header {
|
||||||
|
padding: 12px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
form {
|
||||||
|
flex-direction: column;
|
||||||
|
width: 90%;
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-msg {
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
height: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
.resolution-btn {
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 3px 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,8 @@ describe('HomeComponent', () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [HomeComponent]
|
imports: [HomeComponent],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(HomeComponent);
|
fixture = TestBed.createComponent(HomeComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { HeaderComponent } from '../../shared/components/header/header.component';
|
import { HeaderComponent } from '../../shared/components/header/header.component';
|
||||||
import { FooterComponent } from '../../shared/components/footer/footer.component';
|
import { HeroBannerComponent } from './hero-banner/hero-banner.component';
|
||||||
import { AuthComponent } from '../auth/auth.component';
|
import { CategoriesComponent } from './categories/categories.component';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { MovieService } from '../../services/movie.service';
|
||||||
import { RegisterComponent } from '../auth/register/register.component';
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { LoginComponent } from '../auth/login/login.component';
|
import { VideoPlayerComponent } from './video-player/video-player.component';
|
||||||
import { ForgotPasswordComponent } from '../auth/forgot-password/forgot-password.component';
|
import { BtnSmallComponent } from '../../shared/components/buttons/btn-small/btn-small.component';
|
||||||
import { VerifyEmailComponent } from '../auth/verify-email/verify-email.component';
|
import { UploadMovieComponent } from './upload-movie/upload-movie.component';
|
||||||
import { ErrorToastComponent } from '../../shared/components/error-toast/error-toast.component';
|
import { UserService } from '../../services/user.service';
|
||||||
import { ErrorService } from '../../services/error.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
|
|
@ -17,28 +15,128 @@ import { ErrorService } from '../../services/error.service';
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
FooterComponent,
|
HeroBannerComponent,
|
||||||
AuthComponent,
|
CategoriesComponent,
|
||||||
RegisterComponent,
|
VideoPlayerComponent,
|
||||||
LoginComponent,
|
BtnSmallComponent,
|
||||||
ForgotPasswordComponent,
|
UploadMovieComponent,
|
||||||
VerifyEmailComponent,
|
|
||||||
ErrorToastComponent,
|
|
||||||
],
|
],
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
styleUrl: './home.component.scss',
|
styleUrl: './home.component.scss',
|
||||||
})
|
})
|
||||||
export class HomeComponent implements OnInit {
|
export class HomeComponent implements OnInit {
|
||||||
currentRoute: any;
|
@ViewChild(VideoPlayerComponent) videoPlayer!: VideoPlayerComponent;
|
||||||
|
movies: any[] = [];
|
||||||
|
favoriteMovies: number[] = [];
|
||||||
|
watchedMovies: number[] = [];
|
||||||
|
currentMovie: any[] = [];
|
||||||
|
playMovie: string = '';
|
||||||
|
isLoading: boolean = true;
|
||||||
|
uploadMovieOverview: boolean = false;
|
||||||
|
currentResolution: '360p' | '720p' | '1080p' = '720p';
|
||||||
|
movieIsUploaded: { [resolution: string]: boolean } = {
|
||||||
|
'360': false,
|
||||||
|
'720': false,
|
||||||
|
'1080': false,
|
||||||
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private movieService: MovieService,
|
||||||
public errorService: ErrorService
|
public userService: UserService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
async ngOnInit() {
|
||||||
this.route.url.subscribe((url) => {
|
this.loadLikedAndWatchedMovies();
|
||||||
this.currentRoute = url[0]?.path || '';
|
await this.loadAllMovies();
|
||||||
});
|
if (this.isWideScreen()) {
|
||||||
|
this.currentMovie.length === 0 ? this.loadRandomMovie() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadLikedAndWatchedMovies() {
|
||||||
|
try {
|
||||||
|
const userData = await this.userService.getLikedAndWatchedMovies();
|
||||||
|
this.favoriteMovies = userData.liked_videos;
|
||||||
|
this.watchedMovies = userData.watched_videos;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLikeMovies() {
|
||||||
|
const body = {
|
||||||
|
liked_videos: this.favoriteMovies,
|
||||||
|
};
|
||||||
|
this.userService.updateLikedMovies(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRefreshPage(updatedMovies: any[]) {
|
||||||
|
this.currentMovie = [];
|
||||||
|
setTimeout(() => {
|
||||||
|
this.currentMovie = updatedMovies;
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMoviesChange(updatedMovies: any[]) {
|
||||||
|
if (this.isWideScreen()) {
|
||||||
|
this.loadRandomMovie();
|
||||||
|
} else {
|
||||||
|
this.currentMovie = updatedMovies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isWideScreen() {
|
||||||
|
return window.innerWidth > 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMovieIsUploadedChange(newStatus: { [resolution: string]: boolean }) {
|
||||||
|
this.movieIsUploaded = newStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFavoriteMovieChange(favoriteMovies: any) {
|
||||||
|
this.favoriteMovies = favoriteMovies;
|
||||||
|
this.updateLikeMovies();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeResolution(resolution: '360p' | '720p' | '1080p') {
|
||||||
|
if (this.videoPlayer && this.movieIsUploaded[resolution.replace('p', '')]) {
|
||||||
|
this.videoPlayer.switchResolution(resolution);
|
||||||
|
this.currentResolution = resolution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadAllMovies() {
|
||||||
|
this.isLoading = true;
|
||||||
|
try {
|
||||||
|
this.movies = await this.movieService.getAllMovies();
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeVideo(): void {
|
||||||
|
this.playMovie = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
playVideo(videoPath: string) {
|
||||||
|
this.currentResolution = '720p';
|
||||||
|
this.playMovie = videoPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadRandomMovie(): void {
|
||||||
|
const randomIndex = Math.floor(Math.random() * this.movies.length);
|
||||||
|
this.currentMovie = [this.movies[randomIndex]];
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMovieId(movieId: number) {
|
||||||
|
let index = this.movies.findIndex((movie) => movie.id === movieId);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.currentMovie = [];
|
||||||
|
this.currentMovie.push(this.movies[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleUploadMovieOverview(value: any) {
|
||||||
|
this.uploadMovieOverview = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
@use "./../../../../../assets/style/colors.scss" as *;
|
@use "./../../../../assets/style/colors.scss" as *;
|
||||||
@use "./../../../../../assets/style/form.scss" as *;
|
@use "./../../../../assets/style/form.scss" as *;
|
||||||
@use "./../../../../../assets/style/auth-layout.scss" as *;
|
@use "./../../../../assets/style/auth-layout.scss" as *;
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, EventEmitter, Output } from '@angular/core';
|
import { Component, EventEmitter, Output } from '@angular/core';
|
||||||
import { FormsModule, NgForm } from '@angular/forms';
|
import { FormsModule, NgForm } from '@angular/forms';
|
||||||
import { ErrorService } from '../../../../services/error.service';
|
import { ErrorService } from '../../../services/error.service';
|
||||||
import { BtnLargeComponent } from '../../../../shared/components/buttons/btn-large/btn-large.component';
|
import { BtnLargeComponent } from '../../../shared/components/buttons/btn-large/btn-large.component';
|
||||||
import { MovieService } from '../../../../services/movie.service';
|
import { MovieService } from '../../../services/movie.service';
|
||||||
import { LoadingDialogComponent } from '../../../../shared/components/loading-dialog/loading-dialog.component';
|
import { LoadingDialogComponent } from '../../../shared/components/loading-dialog/loading-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-upload-movie',
|
selector: 'app-upload-movie',
|
||||||
Loading…
Reference in a new issue