Update: Environment for Heroku Production
This commit is contained in:
parent
5fbdb7098e
commit
13daf10ca8
201 changed files with 34 additions and 2736 deletions
|
|
@ -0,0 +1,70 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
|
||||
<app-navbar-user></app-navbar-user><br><br>
|
||||
|
||||
<!-- ---------------------------------------------------------------------------------- -->
|
||||
|
||||
<div style="text-align: center">
|
||||
<input (keyup)="applyFilter($event)" placeholder="Rechercher par mots clés...">
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<!-- ---------------------------------------------------------------------------------- -->
|
||||
|
||||
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
|
||||
<!-- Aperçu Column -->
|
||||
<ng-container matColumnDef="aperçu">
|
||||
<th mat-header-cell *matHeaderCellDef> Aperçu </th>
|
||||
<td mat-cell *matCellDef="let video">
|
||||
<div class="imgsContainer" (click)="onVideo(video)">
|
||||
<img class="imgPlay" src="/assets/play.png">
|
||||
<img class="imgVideo" [src]="video.imageUrl">
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Titre Column -->
|
||||
<ng-container matColumnDef="title">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Titre </th>
|
||||
<td mat-cell *matCellDef="let video"> {{video.title}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Date Column -->
|
||||
<ng-container matColumnDef="date">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date </th>
|
||||
<td mat-cell *matCellDef="let video">
|
||||
{{video.date | date:'dd/LL/YYYY à HH:mm:ss'}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Source Column -->
|
||||
<ng-container matColumnDef="source">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Source </th>
|
||||
<td mat-cell *matCellDef="let video"> {{video.source}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Action Column -->
|
||||
<ng-container matColumnDef="action">
|
||||
<th mat-header-cell *matHeaderCellDef> Action </th>
|
||||
<td mat-cell *matCellDef="let video">
|
||||
<button mat-icon-button (click)="onDelete(video)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
<tr class="mat-row" *matNoDataRow>
|
||||
<td class="mat-cell" colspan="4"> Aucune vidéo ne correspond au filtre: "{{input.value}}" </td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="width: 80%; margin: auto auto">
|
||||
<mat-paginator [pageSizeOptions]="[5, 10, 20]" showFirstLastButtons aria-label="Select page of periodic elements"></mat-paginator>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
.myContainer {
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
|
||||
th.mat-sort-header-sorted {
|
||||
color: black;
|
||||
}
|
||||
|
||||
|
||||
input {
|
||||
width: 35%;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
|
||||
.imgsContainer {
|
||||
position: relative;
|
||||
width: 20vw;
|
||||
height: 15vh;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.imgPlay {
|
||||
position: absolute;
|
||||
margin-left: 9vw;
|
||||
width: 3vw;
|
||||
margin-top: 5vh;
|
||||
height: 6vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.imgVideo {
|
||||
border: solid 1px black;
|
||||
width: 20vw;
|
||||
height: 15vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageHistoryUserComponent } from './page-history-user.component';
|
||||
|
||||
describe('PageHistoriqueComponent', () => {
|
||||
let component: PageHistoryUserComponent;
|
||||
let fixture: ComponentFixture<PageHistoryUserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageHistoryUserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageHistoryUserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import {AfterViewInit, Component, ViewChild} from '@angular/core';
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {MatSort} from "@angular/material/sort";
|
||||
import {MatPaginator} from "@angular/material/paginator";
|
||||
import {Router} from "@angular/router";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-history-user',
|
||||
templateUrl: './page-history-user.component.html',
|
||||
styleUrls: ['./page-history-user.component.scss']
|
||||
})
|
||||
export class PageHistoryUserComponent implements AfterViewInit
|
||||
{
|
||||
displayedColumns: string[] = [ 'aperçu', 'title', 'date', 'source', 'action' ];
|
||||
dataSource ;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
|
||||
|
||||
constructor( public themeService: ThemeService,
|
||||
private messageService: MessageService,
|
||||
private router: Router ) { }
|
||||
|
||||
|
||||
// charge la page
|
||||
ngAfterViewInit(): void
|
||||
{
|
||||
this.messageService
|
||||
.get("user/history")
|
||||
.subscribe(ret => this.ngAfterViewInitCallback(ret), err => this.ngAfterViewInitCallback(err));
|
||||
}
|
||||
|
||||
|
||||
ngAfterViewInitCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
const tabVideoHistory = retour.data.map( video => {
|
||||
return {
|
||||
_id: video._id,
|
||||
videoId: video.videoId,
|
||||
imageUrl: video.imageUrl,
|
||||
title: video.title,
|
||||
date: video.watchedDate,
|
||||
source: video.source,
|
||||
}
|
||||
});
|
||||
this.dataSource = new MatTableDataSource(tabVideoHistory);
|
||||
this.dataSource.sort = this.sort;
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource = this.dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Applique le filtre
|
||||
applyFilter(event: Event): void
|
||||
{
|
||||
const filterValue = (event.target as HTMLInputElement).value;
|
||||
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
// Supprime la video
|
||||
onDelete(video: any): void
|
||||
{
|
||||
this.messageService
|
||||
.put("video/update/"+video._id, { watchedDates: []})
|
||||
.subscribe(ret => this.onDeleteCallback(ret, video), err => this.onDeleteCallback(err, video))
|
||||
}
|
||||
|
||||
|
||||
onDeleteCallback(retour: any, video: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
const index = this.dataSource.data.indexOf(video);
|
||||
this.dataSource.data.splice(index, 1);
|
||||
this.dataSource.data = this.dataSource.data;
|
||||
this.dataSource = this.dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onVideo(video): void
|
||||
{
|
||||
this.messageService
|
||||
.put("video/update/"+video._id, {watchedDate: true})
|
||||
.subscribe(ret => this.onVideoCallback(ret), err => this.onVideoCallback(err));
|
||||
|
||||
const params = {
|
||||
videoId: video.videoId,
|
||||
source: video.source,
|
||||
from: "history",
|
||||
};
|
||||
this.router.navigate(['/user/watching'], { queryParams: params });
|
||||
}
|
||||
|
||||
|
||||
onVideoCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") console.log(retour);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- Navbar -->
|
||||
<div>
|
||||
<app-navbar-user></app-navbar-user>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<!-- --------------------------------------------------------------------- -->
|
||||
|
||||
<!-- [liste des videos] + [liste des playlist] + [pub] -->
|
||||
<mat-grid-list cols="10" rowHeight="85vh">
|
||||
|
||||
<!-- liste des videos -->
|
||||
<mat-grid-tile colspan="4" rowspan="1" class="celluleListeVideo">
|
||||
<app-video-list [playlist]="playlist"></app-video-list>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- liste des playlist -->
|
||||
<mat-grid-tile colspan="4" rowspan="1" class="celluleListePlaylist">
|
||||
<app-playlist-list (eventEmitter)="transmitPlaylistToVideoList($event)"></app-playlist-list>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- pub -->
|
||||
<mat-grid-tile colspan="2" rowspan="1" class="cellulePub">
|
||||
<div class="conteneurPub">
|
||||
<app-advert [ad]="ad" from="myPlaylists"></app-advert>
|
||||
</div>
|
||||
</mat-grid-tile>
|
||||
|
||||
</mat-grid-list>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
.lightTheme {
|
||||
border-color: black;
|
||||
}
|
||||
.darkTheme {
|
||||
border-color: white;
|
||||
}
|
||||
.myContainer {
|
||||
text-align: center;
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
// Liste des vidéos -------------------------------------------------
|
||||
|
||||
.celluleListeVideo {
|
||||
margin: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
// Liste des playlists ---------------------------------------------
|
||||
|
||||
.celluleListePlaylist {
|
||||
margin: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
// Pub -------------------------------------------------------------
|
||||
|
||||
.cellulePub {
|
||||
padding: 0px 10px 0px 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.conteneurPub {
|
||||
//height: 85vh;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
width: 75%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageMyPlaylistsComponent } from './page-my-playlists.component';
|
||||
|
||||
describe('PageMesPlaylistsComponent', () => {
|
||||
let component: PageMyPlaylistsComponent;
|
||||
let fixture: ComponentFixture<PageMyPlaylistsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageMyPlaylistsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageMyPlaylistsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {HttpParams} from "@angular/common/http";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-my-playlists',
|
||||
templateUrl: './page-my-playlists.component.html',
|
||||
styleUrls: ['./page-my-playlists.component.scss']
|
||||
})
|
||||
export class PageMyPlaylistsComponent implements OnInit
|
||||
{
|
||||
ad; // pub
|
||||
playlist: any; // la playlist sélectionnée
|
||||
|
||||
|
||||
constructor( public themeService: ThemeService,
|
||||
private messageService: MessageService ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
let params = new HttpParams();
|
||||
params = params.append("quantity", 1);
|
||||
this.messageService
|
||||
.get("user/ad", params)
|
||||
.subscribe(ret => this.adCallback(ret), err => this.adCallback(err));
|
||||
}
|
||||
|
||||
|
||||
adCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.ad = retour.data[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
transmitPlaylistToVideoList(playlist): void
|
||||
{
|
||||
if ((playlist === null) || (playlist === undefined)) {
|
||||
this.playlist = playlist;
|
||||
}
|
||||
else {
|
||||
this.messageService
|
||||
.get("playlist/findOne/" + playlist.id)
|
||||
.subscribe(ret => this.afterReceivingPlaylistWithVideo(ret, playlist), err => this.afterReceivingPlaylistWithVideo(err, playlist));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
afterReceivingPlaylistWithVideo(retour: any, playlist): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.playlist = playlist;
|
||||
}
|
||||
else {
|
||||
this.playlist = retour.data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- Search bar -->
|
||||
<div class="row searchBarContainer">
|
||||
<div style="text-align: center">
|
||||
<input type="search" placeholder="Rechercher..." class="inputSearchBar" [(ngModel)]="search" (ngModelChange)="whileSearch()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Liste des playlist -->
|
||||
<div class="row" style="margin: 0px">
|
||||
<div class="playlistListContainer">
|
||||
<div *ngFor="let playlist of tabPlaylist" class="playlistContainer">
|
||||
|
||||
<div [class]="getClassOfPlaylistContainer(playlist)" (click)="eventEmitter.emit(playlist); playlistFocusedOn = playlist">
|
||||
<div class="col-2" style="text-align: left">
|
||||
<button mat-icon-button (click)="onUpdatePlaylist(playlist)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-8" style="text-align: center">
|
||||
<span class="playlistName"> {{playlist.name}} </span><br>
|
||||
<span class="playListCount" *ngIf="playlist.videoIds.length <= 1"> {{playlist.videoIds.length}} vidéo </span>
|
||||
<span class="playListCount" *ngIf="playlist.videoIds.length > 1"> {{playlist.videoIds.length}} vidéos </span>
|
||||
</div>
|
||||
<div class="col-2" style="text-align: right">
|
||||
<button mat-icon-button (click)="onDeletePlaylist(playlist)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Bouton creer playlist-->
|
||||
<div class="row btnCreerPlaylistContainer" >
|
||||
<button mat-button class="btnCreerPlaylist" (click)="onCreatePlaylist()"> Créer une playlist </button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
.myContainer {
|
||||
background-color: white ;
|
||||
text-align: center;
|
||||
width: 35vw;
|
||||
margin: 1vh 0vh 3vh 0vh;
|
||||
padding: 0px;
|
||||
border: solid 2px black;
|
||||
border-radius: 10px;
|
||||
box-shadow: 10px 5px 5px black;
|
||||
}
|
||||
|
||||
// SearchBar -----------------------------------------------------------
|
||||
|
||||
.searchBarContainer {
|
||||
text-align: center;
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 10px 0px 10px 0px;
|
||||
//background-color: #dcdcdc;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
font-size: large;
|
||||
border-bottom: solid 1px black;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
|
||||
.inputSearchBar {
|
||||
width: 70%;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
// Liste des playlists -------------------------------------------------
|
||||
|
||||
.playlistListContainer {
|
||||
max-width: 100%;
|
||||
height: 60vh;
|
||||
overflow-y: scroll;
|
||||
padding: 0px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.playlistContainer {
|
||||
max-width: 100%;
|
||||
padding: 0px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
||||
.btnPlaylist {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-bottom: solid 1px black;
|
||||
//width: 100%;
|
||||
width: 35vw;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.btnPlaylist:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.btnPlaylistFocus {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.btnPlaylistFocus:hover {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
|
||||
.playListCount {
|
||||
color: gray;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
// Bouton creer playlist -------------------------------------------------
|
||||
|
||||
.btnCreerPlaylistContainer {
|
||||
margin: 0px 0px 0px 0px;
|
||||
background-color: #dcdcdc;
|
||||
font-size: large;
|
||||
border-top: solid 1px black;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
|
||||
.btnCreerPlaylist {
|
||||
margin: 0px 0px 0px 0px;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
//background: linear-gradient(180deg, #e6e6e6 0%, rgba(0,0,0,0.25) 49%, rgba(38,38,38,0.6) 51%, rgba(0,0,0,0.25) 100%);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PlaylistListComponent } from './playlist-list.component';
|
||||
|
||||
describe('PlaylistListComponent', () => {
|
||||
let component: PlaylistListComponent;
|
||||
let fixture: ComponentFixture<PlaylistListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PlaylistListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PlaylistListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {PopupCreateOrUpdatePlaylistComponent} from "../popup-create-or-update-playlist/popup-create-or-update-playlist.component";
|
||||
import {PopupDeletePlaylistComponent} from "../popup-delete-playlist/popup-delete-playlist.component";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-playlist-list',
|
||||
templateUrl: './playlist-list.component.html',
|
||||
styleUrls: ['./playlist-list.component.scss']
|
||||
})
|
||||
export class PlaylistListComponent implements OnInit
|
||||
{
|
||||
allPlaylists: any[] = []; // toutes les playlists
|
||||
@Output() eventEmitter = new EventEmitter<any>(); // pour envoyer au parent la playlist selectionner
|
||||
search: string = "" ; // contenu de la barre de recherche
|
||||
tabPlaylist: any[] = []; // playlist affichées
|
||||
playlistFocusedOn: any;
|
||||
|
||||
|
||||
constructor( public themeService: ThemeService,
|
||||
public dialog: MatDialog,
|
||||
public snackBar: MatSnackBar,
|
||||
private messageService: MessageService ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.messageService
|
||||
.get("playlist/findAll")
|
||||
.subscribe( retour => this.ngOnInitCallback(retour), err => this.ngOnInitCallback(err) );
|
||||
}
|
||||
|
||||
|
||||
ngOnInitCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
} else {
|
||||
const aux = retour.data.filter( x => x.isActive === true);
|
||||
this.allPlaylists = aux.map(x => {
|
||||
x["_id"] = x.id ;
|
||||
return x;
|
||||
});
|
||||
this.tabPlaylist = [].concat(this.allPlaylists);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// s'execute lorsqu'on écrit sur la barre de recherche
|
||||
whileSearch()
|
||||
{
|
||||
this.tabPlaylist = [];
|
||||
for(let playlist of this.allPlaylists)
|
||||
{
|
||||
if(playlist.name.includes(this.search)) this.tabPlaylist.push(playlist);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// click sur créer playlist
|
||||
onCreatePlaylist(): void
|
||||
{
|
||||
const config = {
|
||||
data: {
|
||||
action: "create",
|
||||
tabPlaylist: this.tabPlaylist,
|
||||
}
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupCreateOrUpdatePlaylistComponent, config )
|
||||
.afterClosed()
|
||||
.subscribe(playlist => {
|
||||
|
||||
const config = { duration: 1500, panelClass: "custom-class" };
|
||||
if((playlist === null) || (playlist === undefined)) {
|
||||
this.snackBar.open("Opération annulée", "", config);
|
||||
}
|
||||
else {
|
||||
playlist["_id"] = playlist.id;
|
||||
this.allPlaylists.push(playlist);
|
||||
this.tabPlaylist.push(playlist);
|
||||
this.snackBar.open(`La playlist '${playlist.name}' a bien été créée ✔`, "", config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// click sur update playlist
|
||||
onUpdatePlaylist(playlistToUpdate): void
|
||||
{
|
||||
const config = {
|
||||
data: {
|
||||
action: "update",
|
||||
tabPlaylist: this.tabPlaylist,
|
||||
playlistName: playlistToUpdate.name,
|
||||
playlistId: playlistToUpdate._id
|
||||
}
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupCreateOrUpdatePlaylistComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe(newName => {
|
||||
|
||||
const config = { duration: 1500, panelClass: "custom-class" };
|
||||
if((newName === null) || (newName === undefined)) {
|
||||
this.snackBar.open("Opération annulée", "", config);
|
||||
}
|
||||
else {
|
||||
let index = this.allPlaylists.findIndex( elt => (elt._id === playlistToUpdate._id));
|
||||
this.allPlaylists[index].name = newName;
|
||||
index = this.tabPlaylist.findIndex( elt => (elt._id === playlistToUpdate._id));
|
||||
this.tabPlaylist[index].name = newName;
|
||||
this.snackBar.open(`La playlist '${playlistToUpdate.name}' a bien été mise à jour ✔`, "", config);
|
||||
this.eventEmitter.emit(this.tabPlaylist[index]);
|
||||
this.playlistFocusedOn = this.tabPlaylist[index]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// click sur supprimer playlist
|
||||
onDeletePlaylist(playlist): void
|
||||
{
|
||||
const config = {data: playlist};
|
||||
this.dialog
|
||||
.open(PopupDeletePlaylistComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe(retour => {
|
||||
|
||||
const config = { duration: 1500, panelClass: "custom-class" };
|
||||
if((retour === null) || (retour === undefined)) {
|
||||
this.snackBar.open("Opération annulée", "", config);
|
||||
}
|
||||
else {
|
||||
let index = this.allPlaylists.indexOf(playlist);
|
||||
if(index >= 0) this.allPlaylists.splice(index, 1);
|
||||
|
||||
index = this.tabPlaylist.indexOf(playlist);
|
||||
if(index >= 0) this.tabPlaylist.splice(index, 1);
|
||||
|
||||
this.eventEmitter.emit(null);
|
||||
this.playlistFocusedOn = null;
|
||||
this.snackBar.open(`La playlist '${playlist.name}' a bien été suprimée ✔`, "", config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// retourne la class CSS de conteneur de playlist
|
||||
getClassOfPlaylistContainer(playlist): string
|
||||
{
|
||||
if(playlist === this.playlistFocusedOn) return "row btnPlaylist btnPlaylistFocus" ;
|
||||
else return "row btnPlaylist" ;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<div style="text-align: center; margin: 0px; padding: 0px">
|
||||
|
||||
<div style="margin: 0px; padding: 0px">
|
||||
<mat-form-field appearance="fill" style="margin: 0px; padding: 0px">
|
||||
<mat-label> Nom de la playlist </mat-label>
|
||||
<input matInput [(ngModel)]="name" style="width: 100%">
|
||||
</mat-form-field>
|
||||
<span *ngIf="hasError" class="mat-error"> {{errorMessage}} </span>
|
||||
</div>
|
||||
|
||||
<div style="text-align: right ; margin: 0px; padding: 0px">
|
||||
<button mat-button (click)="onAnnuler()">Annuler</button>
|
||||
<button mat-button (click)="onValider()">
|
||||
<span *ngIf="action === 'create'"> Créer </span>
|
||||
<span *ngIf="action === 'update'"> Modifier </span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupCreateOrUpdatePlaylistComponent } from './popup-create-or-update-playlist.component';
|
||||
|
||||
describe('PopupCreatePlaylistComponent', () => {
|
||||
let component: PopupCreateOrUpdatePlaylistComponent;
|
||||
let fixture: ComponentFixture<PopupCreateOrUpdatePlaylistComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupCreateOrUpdatePlaylistComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupCreateOrUpdatePlaylistComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-create-or-update-playlist',
|
||||
templateUrl: './popup-create-or-update-playlist.component.html',
|
||||
styleUrls: ['./popup-create-or-update-playlist.component.scss']
|
||||
})
|
||||
export class PopupCreateOrUpdatePlaylistComponent implements OnInit
|
||||
{
|
||||
name: string = "" ;
|
||||
hasError: boolean = false;
|
||||
tabNomPlaylist: string[] = [];
|
||||
errorMessage: string = "" ;
|
||||
action: string = "";
|
||||
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupCreateOrUpdatePlaylistComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
private messageService: MessageService) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.action = this.data.action;
|
||||
this.tabNomPlaylist = this.data.tabPlaylist.map( playlist0 => playlist0.name );
|
||||
if(this.action === "update") this.name = this.data.playlistName;
|
||||
}
|
||||
|
||||
|
||||
onValider(): void
|
||||
{
|
||||
this.checkError();
|
||||
if(!this.hasError)
|
||||
{
|
||||
if(this.action === "create")
|
||||
{
|
||||
this.messageService
|
||||
.post("playlist/create", {name: this.name})
|
||||
.subscribe(retour => this.onValiderCallback(retour), err => this.onValiderCallback(err));
|
||||
}
|
||||
else if(this.action === "update")
|
||||
{
|
||||
this.messageService
|
||||
.put("playlist/update/"+this.data.playlistId, {name: this.name})
|
||||
.subscribe(retour => this.onValiderCallback(retour), err => this.onValiderCallback(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onValiderCallback(retour): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
else {
|
||||
if(this.action === "create") this.dialogRef.close(retour.data);
|
||||
else if(this.action === "update") this.dialogRef.close(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
checkError(): void
|
||||
{
|
||||
if(this.name === "") {
|
||||
this.errorMessage = "Le nom ne peut pas être vide" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.tabNomPlaylist.includes(this.name)){
|
||||
this.errorMessage = "Ce nom est déjà utilisé" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.hasError = false;
|
||||
this.errorMessage = "" ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onAnnuler(): void
|
||||
{
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<mat-dialog-content class="mat-typography">
|
||||
Êtes-vous sûr de vouloir supprimer <i>{{playlist.name}}</i> ?
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="dialogRef.close()">Annuler</button>
|
||||
<button mat-button (click)="onValidate()" cdkFocusInitial>Valider</button>
|
||||
</mat-dialog-actions>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupDeletePlaylistComponent } from './popup-delete-playlist.component';
|
||||
|
||||
describe('PopupDeletePlaylistComponent', () => {
|
||||
let component: PopupDeletePlaylistComponent;
|
||||
let fixture: ComponentFixture<PopupDeletePlaylistComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupDeletePlaylistComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupDeletePlaylistComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-delete-playlist',
|
||||
templateUrl: './popup-delete-playlist.component.html',
|
||||
styleUrls: ['./popup-delete-playlist.component.scss']
|
||||
})
|
||||
export class PopupDeletePlaylistComponent implements OnInit
|
||||
{
|
||||
playlist;
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupDeletePlaylistComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
private messageService: MessageService ) { }
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.playlist = this.data;
|
||||
}
|
||||
|
||||
onValidate(): void
|
||||
{
|
||||
this.messageService
|
||||
.delete("playlist/delete/"+this.playlist._id)
|
||||
.subscribe( retour => this.onValidateCallback(retour), err => this.onValidateCallback(err));
|
||||
}
|
||||
|
||||
onValidateCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
else {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- Bordure haute -->
|
||||
<div class="row topBorder">
|
||||
|
||||
<!-- PlaylistDB existe ? -->
|
||||
<div *ngIf="(playlist !== null) && (playlist !== undefined); then ok1 else nope1"></div>
|
||||
|
||||
<!-- oui -->
|
||||
<ng-template #ok1>
|
||||
<span class="spanPlayListTitle"> {{playlist.name}} </span>
|
||||
</ng-template>
|
||||
|
||||
<!-- non -->
|
||||
<ng-template #nope1>
|
||||
<span class="spanPlayListTitle"> Aucune playlist selectionnée </span>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- --------------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- Liste des videos -->
|
||||
<div class="row" style="margin: 0px; padding: 0px">
|
||||
<div class="listVideoContainer">
|
||||
|
||||
<!-- 'playlist' existe ? -->
|
||||
<div *ngIf="(playlist !== null) && (playlist !== undefined); then ok2 else nope2"></div>
|
||||
|
||||
<!-- oui, 'playlist' existe -->
|
||||
<ng-template #ok2>
|
||||
<div *ngFor="let video of videosInPlaylist; let i = index;" class="videoContainer">
|
||||
|
||||
<mat-grid-list cols="12" rowHeight="15vh">
|
||||
|
||||
<!-- btnAdd -->
|
||||
<mat-grid-tile [colspan]="2" [rowspan]="1">
|
||||
<button mat-icon-button (click)="onAddToPlaylist(video)">
|
||||
<mat-icon> add_circle </mat-icon>
|
||||
</button>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- imgVideo -->
|
||||
<mat-grid-tile [colspan]="8" [rowspan]="1">
|
||||
<div style="margin: auto; width: 20vw; height: 15vh;">
|
||||
<div class="imgsContainer" (click)="onVideo(video)">
|
||||
<img class="imgPlay" src="/assets/play.png">
|
||||
<img class="imgVideo" [src]="video.imageUrl">
|
||||
</div>
|
||||
</div>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- btnDelete -->
|
||||
<mat-grid-tile [colspan]="2" [rowspan]="1">
|
||||
<button mat-icon-button (click)="onDelete(video, i)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</mat-grid-tile>
|
||||
|
||||
</mat-grid-list>
|
||||
|
||||
<!-- titre video -->
|
||||
<span style="margin-top: 10px">
|
||||
{{video.title}}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<!-- non, 'playlist' n'existe pas -->
|
||||
<ng-template #nope2>
|
||||
<div></div>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- --------------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- Bordure basse -->
|
||||
<div class="row bottomBorder">
|
||||
<div style="visibility: hidden">a</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
.myContainer {
|
||||
//background-color: white ;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
text-align: center;
|
||||
width: 35vw;
|
||||
margin: 1vh 0vh 3vh 0vh;
|
||||
padding: 0px;
|
||||
border: solid 2px black;
|
||||
border-radius: 10px;
|
||||
box-shadow: 10px 5px 5px black;
|
||||
}
|
||||
|
||||
// TopBorder --------------------------------------------------------
|
||||
|
||||
.topBorder {
|
||||
margin: 0px 0px 0px 0px;
|
||||
//background-color: #dcdcdc;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
text-align: left;
|
||||
padding: 5px 0px 5px 5px;
|
||||
border-bottom: solid 1px black;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
|
||||
.spanPlayListTitle {
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
// Liste des videos ------------------------------------------------
|
||||
|
||||
.listVideoContainer {
|
||||
height: 65vh;
|
||||
background-color: white;
|
||||
padding: 0px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.videoContainer {
|
||||
border-bottom: solid 1px black;
|
||||
padding: 15px 0px 15px 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.imgsContainer {
|
||||
position: relative;
|
||||
width: 20vw;
|
||||
height: 15vh;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.imgPlay {
|
||||
position: absolute;
|
||||
margin-left: 9vw;
|
||||
width: 3vw;
|
||||
margin-top: 5vh;
|
||||
height: 6vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.imgVideo {
|
||||
border: solid 1px black;
|
||||
width: 20vw;
|
||||
height: 15vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
// BottomBorder --------------------------------------------------------
|
||||
|
||||
.bottomBorder {
|
||||
margin: 0px 0px 0px 0px;
|
||||
//background-color: #dcdcdc;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
border-top: solid 1px black;
|
||||
border-bottom: solid 1px black;
|
||||
font-size: large;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { VideoListComponent } from './video-list.component';
|
||||
|
||||
describe('VideoListComponent', () => {
|
||||
let component: VideoListComponent;
|
||||
let fixture: ComponentFixture<VideoListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ VideoListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(VideoListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
92
src/app/user/myPlaylists/video-list/video-list.component.ts
Normal file
92
src/app/user/myPlaylists/video-list/video-list.component.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
|
||||
import {AddVideoToPlaylistsService} from "../../utils/services/addVideoToPlaylists/add-video-to-playlists.service";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {Router} from "@angular/router";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-video-list',
|
||||
templateUrl: './video-list.component.html',
|
||||
styleUrls: ['./video-list.component.scss']
|
||||
})
|
||||
export class VideoListComponent implements OnChanges
|
||||
{
|
||||
@Input() playlist: any;
|
||||
videosInPlaylist: any[] = [];
|
||||
|
||||
|
||||
constructor( private messageService: MessageService,
|
||||
public themeService: ThemeService,
|
||||
private addVideoToPlaylistsService: AddVideoToPlaylistsService,
|
||||
private snackBar: MatSnackBar,
|
||||
private profilService: ProfilService,
|
||||
private router: Router ) { }
|
||||
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void
|
||||
{
|
||||
if((this.playlist !== null) && (this.playlist !== undefined)) this.videosInPlaylist = this.playlist.videos;
|
||||
}
|
||||
|
||||
|
||||
onAddToPlaylist(video: any): void
|
||||
{
|
||||
this.addVideoToPlaylistsService.run(video.videoId, video.source, video.interest);
|
||||
}
|
||||
|
||||
|
||||
onDelete(video0: any, indexVideo: number): void
|
||||
{
|
||||
const data = {
|
||||
videoId: {
|
||||
id: video0._id,
|
||||
action: "delete"
|
||||
}
|
||||
}
|
||||
this.messageService
|
||||
.put("playlist/update/"+this.playlist._id, data)
|
||||
.subscribe( ret => this.onDeleteCallback(ret, indexVideo), err => this.onDeleteCallback(err, indexVideo));
|
||||
}
|
||||
|
||||
|
||||
onDeleteCallback(retour: any, indexVideo: number): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.playlist.videos.splice(indexVideo, 1);
|
||||
this.videosInPlaylist.splice(indexVideo, 1);
|
||||
let message = "La video a bien été supprimé de la playlist";
|
||||
const config = { duration: 1000, panelClass: "custom-class" };
|
||||
this.snackBar.open( message, "", config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onVideo(video: any): void
|
||||
{
|
||||
this.messageService
|
||||
.put("video/update/"+video._id, {watchedDate: true})
|
||||
.subscribe(ret => this.onVideoCallback(ret), err => this.onVideoCallback(err));
|
||||
|
||||
const params = {
|
||||
videoId: video.videoId,
|
||||
source: video.source,
|
||||
_idPlaylist: this.playlist._id,
|
||||
from: "myPlaylists",
|
||||
};
|
||||
this.router.navigate(['/user/watching'], { queryParams: params });
|
||||
}
|
||||
|
||||
|
||||
onVideoCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") console.log(retour);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<mat-form-field class="example-chip-list" appearance="fill">
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
<mat-chip-list #chipList aria-label="Fruit selection">
|
||||
|
||||
<mat-chip
|
||||
*ngFor="let interest of myInterests"
|
||||
[selectable]="selectable"
|
||||
[removable]="removable"
|
||||
(removed)="remove(interest)">
|
||||
{{interest}}
|
||||
<button matChipRemove *ngIf="removable">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</mat-chip>
|
||||
|
||||
<input
|
||||
placeholder="Tapez un centre d'intérêt"
|
||||
#tagInput
|
||||
[formControl]="formControl"
|
||||
[matAutocomplete]="auto"
|
||||
[matChipInputFor]="chipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
(matChipInputTokenEnd)="add($event)">
|
||||
|
||||
</mat-chip-list>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
|
||||
<mat-option *ngFor="let interest of filteredInterests | async" [value]="interest">
|
||||
{{interest}}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
</mat-form-field>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
mat-form-field {
|
||||
width: 100%;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
mat-chip-list {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
mat-chip {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
mat-option {
|
||||
font-size: small;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { InputInterestsProfilComponent } from './input-interests-profil.component';
|
||||
|
||||
describe('InputInterestsComponent', () => {
|
||||
let component: InputInterestsProfilComponent;
|
||||
let fixture: ComponentFixture<InputInterestsProfilComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ InputInterestsProfilComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(InputInterestsProfilComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
|
||||
import {COMMA, ENTER} from "@angular/cdk/keycodes";
|
||||
import {FormControl} from "@angular/forms";
|
||||
import {Observable} from "rxjs";
|
||||
import {map, startWith} from "rxjs/operators";
|
||||
import {MatChipInputEvent} from "@angular/material/chips";
|
||||
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-input-interests-profil',
|
||||
templateUrl: './input-interests-profil.component.html',
|
||||
styleUrls: ['./input-interests-profil.component.scss']
|
||||
})
|
||||
export class InputInterestsProfilComponent implements OnInit
|
||||
{
|
||||
selectable = true;
|
||||
removable = true;
|
||||
separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||
formControl = new FormControl();
|
||||
filteredInterests: Observable<string[]>;
|
||||
@Input() myInterests: string[] = [];
|
||||
allInterests: string[] = [];
|
||||
@Output() eventEmitter = new EventEmitter<string[]>();
|
||||
@ViewChild('tagInput') tagInput: ElementRef<HTMLInputElement>;
|
||||
interestsNotSelected: string[] = [];
|
||||
|
||||
|
||||
constructor( private messageService: MessageService ) {}
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.filteredInterests = this.formControl.valueChanges.pipe(
|
||||
startWith(null),
|
||||
map((fruit: string | null) => fruit ? this._filter(fruit) : this.interestsNotSelected.slice()));
|
||||
|
||||
this.messageService
|
||||
.get("misc/getInterests")
|
||||
.subscribe( retour => {
|
||||
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.allInterests = [];
|
||||
for(let elt of retour.data)
|
||||
{
|
||||
this.allInterests.push(elt.interest);
|
||||
this.interestsNotSelected.push(elt.interest);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
add(event: MatChipInputEvent): void
|
||||
{
|
||||
const value = (event.value || '').trim();
|
||||
const index = this.interestsNotSelected.indexOf(value);
|
||||
if (value && (index !== -1) && (!this.myInterests.includes(value)))
|
||||
{
|
||||
this.myInterests.push(value);
|
||||
event.chipInput!.clear();
|
||||
this.formControl.setValue(null);
|
||||
this.eventEmitter.emit(this.myInterests);
|
||||
this.interestsNotSelected.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remove(interest: string): void
|
||||
{
|
||||
// supprimer 'interest' de 'myInterest'
|
||||
const index = this.myInterests.indexOf(interest);
|
||||
if (index >= 0) this.myInterests.splice(index, 1);
|
||||
this.eventEmitter.emit(this.myInterests);
|
||||
|
||||
// remmettre 'interest' dans 'interestsNotSelected'
|
||||
if(!this.interestsNotSelected.includes(interest))
|
||||
{
|
||||
const indexOfAutres = this.interestsNotSelected.indexOf("Autres");
|
||||
if(indexOfAutres !== -1)
|
||||
{
|
||||
this.interestsNotSelected.splice(indexOfAutres, 1);
|
||||
if(interest !== "Autres") this.interestsNotSelected.push(interest);
|
||||
this.interestsNotSelected.sort();
|
||||
this.interestsNotSelected.push("Autres");
|
||||
}
|
||||
else {
|
||||
this.interestsNotSelected.push(interest);
|
||||
if(interest !== "Autres") this.interestsNotSelected.sort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
selected(event: MatAutocompleteSelectedEvent): void
|
||||
{
|
||||
const value = event.option.viewValue;
|
||||
if(!this.myInterests.includes(value))
|
||||
{
|
||||
this.myInterests.push(value);
|
||||
const index = this.interestsNotSelected.indexOf(value);
|
||||
this.interestsNotSelected.splice(index, 1);
|
||||
}
|
||||
this.tagInput.nativeElement.value = '';
|
||||
this.formControl.setValue(null);
|
||||
this.eventEmitter.emit(this.myInterests);
|
||||
}
|
||||
|
||||
|
||||
private _filter(value: string): string[]
|
||||
{
|
||||
const filterValue = value.toLowerCase();
|
||||
return this.interestsNotSelected.filter(fruit => fruit.toLowerCase().includes(filterValue));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- NavBar -->
|
||||
<app-navbar-user></app-navbar-user>
|
||||
|
||||
<!-- Boite -->
|
||||
<div class="boite">
|
||||
|
||||
<!-- Photo de profil -->
|
||||
<div style="text-align: center">
|
||||
<img [src]="user.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'">
|
||||
</div>
|
||||
|
||||
<!-- Col gauche & droite -->
|
||||
<div class="row">
|
||||
<ng-template *ngIf="true; then leftCol"></ng-template>
|
||||
<ng-template *ngIf="true; then rightCol"></ng-template>
|
||||
</div>
|
||||
|
||||
<!-- Modifier profil -->
|
||||
<div class="btnContainer">
|
||||
<button mat-button class="myBtn" (click)="onModifier()">Modifier profil</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- --------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<ng-template #leftCol>
|
||||
<div class="col-6">
|
||||
|
||||
<!-- login -->
|
||||
<div class="row myRow">
|
||||
<div class="col-8 myLabel">Pseudo:</div>
|
||||
<div class="col-4 myValue"> {{user.login}} </div>
|
||||
</div>
|
||||
|
||||
<!-- email -->
|
||||
<div class="row myRow">
|
||||
<div class="col-8 myLabel">Mail:</div>
|
||||
<div class="col-4 myValue"> {{user.email}} </div>
|
||||
</div>
|
||||
|
||||
<!-- gender -->
|
||||
<div class="row myRow">
|
||||
<div class="col-8 myLabel">Sexe:</div>
|
||||
<div class="col-4 myValue">
|
||||
<span *ngIf="user.gender==='man'"> Homme </span>
|
||||
<span *ngIf="user.gender==='woman'"> Femme </span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- createdAt -->
|
||||
<div class="row myRow">
|
||||
<div class="col-8 myLabel">Date de création:</div>
|
||||
<div class="col-4 myValue">{{ user.createdAt | date:'dd/LL/YYYY' }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
||||
<!-- --------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<ng-template #rightCol>
|
||||
<div class="col-6" style="border-left: solid 1px #e6e6e6">
|
||||
|
||||
<!-- dateOfBirth -->
|
||||
<div class="row myRow">
|
||||
<div class="col-4 myLabel">Date de naissance:</div>
|
||||
<div class="col-8 myValue">{{ user.dateOfBirth | date:'dd/LL/YYYY' }}</div>
|
||||
</div>
|
||||
|
||||
<!-- interets -->
|
||||
<div class="row myRow">
|
||||
<div class="col-4 myLabel">Centres d'intérêt:</div>
|
||||
<div class="col-8 myValue">
|
||||
<div class="interestsContainer">
|
||||
<div *ngFor="let interest of user.interests" class="interest"> {{interest}} </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
.myContainer {
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
||||
.boite {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 70%;
|
||||
margin-top: 10vh;
|
||||
border: solid 3px;
|
||||
border-radius: 10px;
|
||||
padding: 20px 40px 20px 40px;
|
||||
background-color: #ffffff;
|
||||
text-align: center;
|
||||
box-shadow: 10px 5px 5px black;
|
||||
}
|
||||
.lightTheme .boite {
|
||||
border-color: black;
|
||||
}
|
||||
.darkTheme .boite {
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
img {
|
||||
margin: 0px 0px 10px 0px;
|
||||
width: 5vw;
|
||||
height: 5vw;
|
||||
border: solid 2px black;
|
||||
border-radius: 50%;
|
||||
font-size: xxx-large;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
.myRow {
|
||||
margin: 15px 0px 15px 0px;
|
||||
}
|
||||
.myLabel {
|
||||
text-align: right;
|
||||
padding: 0px 5px 0px 0px;
|
||||
margin: 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.myValue {
|
||||
text-align: left;
|
||||
padding: 0px 0px 0px 5px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
.interestsContainer {
|
||||
width: 70%;
|
||||
height: 15vh;
|
||||
overflow-y: scroll;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.interest {
|
||||
border-bottom: 1px solid #dcdcdc;
|
||||
padding: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
.btnContainer {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
.myBtn {
|
||||
border: solid 1px black;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageProfilUserComponent } from './page-profil-user.component';
|
||||
|
||||
describe('PageProfilUserComponent', () => {
|
||||
let component: PageProfilUserComponent;
|
||||
let fixture: ComponentFixture<PageProfilUserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageProfilUserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageProfilUserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {PopupUpdateUserComponent} from "../popup-update-user/popup-update-user.component";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-profil-user',
|
||||
templateUrl: './page-profil-user.component.html',
|
||||
styleUrls: ['./page-profil-user.component.scss']
|
||||
})
|
||||
export class PageProfilUserComponent implements OnInit
|
||||
{
|
||||
user = {
|
||||
_id: "",
|
||||
login: "",
|
||||
hashPass: "",
|
||||
email: "",
|
||||
role: {
|
||||
name: "user",
|
||||
permission: 0,
|
||||
isAccepted: false,
|
||||
},
|
||||
profileImageUrl: "",
|
||||
dateOfBirth: null,
|
||||
gender: "man",
|
||||
interests: [],
|
||||
company: "",
|
||||
isActive: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
lastConnexion: null
|
||||
};
|
||||
|
||||
|
||||
constructor( public themeService: ThemeService,
|
||||
public dialog: MatDialog,
|
||||
private snackBar: MatSnackBar,
|
||||
private messageService: MessageService,
|
||||
private profilService: ProfilService ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.messageService
|
||||
.get( "user/findOne/"+this.profilService.getId())
|
||||
.subscribe( retour => this.ngOnInitCallback(retour), err => this.ngOnInitCallback(err) )
|
||||
}
|
||||
|
||||
|
||||
ngOnInitCallback(retour: any)
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.user = retour.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onModifier()
|
||||
{
|
||||
const config = {
|
||||
width: '70%',
|
||||
data: { user: this.user }
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupUpdateUserComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe(retour => {
|
||||
|
||||
if((retour === null) || (retour === undefined))
|
||||
{
|
||||
const config = { duration: 1000, panelClass: "custom-class" };
|
||||
this.snackBar.open( "Opération annulé", "", config);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.user = retour;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<div class="myContainer">
|
||||
|
||||
<!-- Photo de profil -->
|
||||
<div style="text-align: center; font-size: small">
|
||||
<img [src]="userCopy.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'"><br>
|
||||
<input title="lien vers image"
|
||||
type="text"
|
||||
class="form-control inputUrlImage"
|
||||
style="font-size: small"
|
||||
[(ngModel)]="userCopy.profileImageUrl">
|
||||
</div><br>
|
||||
|
||||
<!-- Col gauche & droite -->
|
||||
<div class="row">
|
||||
<ng-template *ngIf="true; then leftCol"></ng-template>
|
||||
<ng-template *ngIf="true; then rightCol"></ng-template>
|
||||
</div>
|
||||
|
||||
<!-- message d'erreur -->
|
||||
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
|
||||
<span class="mat-error"><br>{{errorMessage}}</span>
|
||||
</div>
|
||||
|
||||
<!-- Boutons -->
|
||||
<div style="width: 100%; text-align: right">
|
||||
<button mat-button (click)="this.dialogRef.close(null)"> Annuler </button>
|
||||
<button mat-button (click)="onValider()"> Enregistrer </button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ----------------------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- Colonne gauche -->
|
||||
<ng-template #leftCol>
|
||||
<div class="col-6">
|
||||
|
||||
<!-- login -->
|
||||
<div class="form-group row myRow">
|
||||
<label for="login" class="col-sm-6 col-form-label myLeftLabel">Pseudo:</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control myValue" id="login" [(ngModel)]="userCopy.login">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- dateOfBirth -->
|
||||
<div class="form-group row myRow">
|
||||
<label for="dateOfBirth" class="col-sm-6 col-form-label myLeftLabel">Date de naissance:</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="date"
|
||||
class="form-control myValue"
|
||||
id="dateOfBirth"
|
||||
[ngModel] ="userCopy.dateOfBirth | date:'yyyy-MM-dd'"
|
||||
(ngModelChange)="userCopy.dateOfBirth = $event">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- sexe -->
|
||||
<div class="myRow" style="text-align: right; margin-right: 15px">
|
||||
<mat-radio-group [(ngModel)]="userCopy.gender">
|
||||
<mat-radio-button value="man"> Homme </mat-radio-button>
|
||||
<mat-radio-button value="woman"> Femme </mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
|
||||
<!-- Modifier mot de passe -->
|
||||
<div class="form-group row" style="margin: 10px 0px 10px 0px">
|
||||
<label for="changePassword" class="col-sm-6 col-form-label myLeftLabel">Modifier mot de passe:</label>
|
||||
<div class="col-sm-6">
|
||||
<mat-checkbox id="changePassword" [(ngModel)]="changePassword"></mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nouveau mot de passe -->
|
||||
<div class="form-group row" style="margin: 10px 0px 10px 0px">
|
||||
<label for="newPassword" class="col-sm-6 col-form-label myLeftLabel"></label>
|
||||
<div class="col-sm-6">
|
||||
<input type="password"
|
||||
class="form-control myValue"
|
||||
id="newPassword"
|
||||
placeholder=" Nouveau mot de passe"
|
||||
[(ngModel)]="newPassword"
|
||||
[disabled]="!changePassword">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirmation nouveau mot de passe -->
|
||||
<div class="form-group row" style="margin: 10px 0px 10px 0px">
|
||||
<label for="confimNewPassword" class="col-sm-6 col-form-label myLeftLabel"></label>
|
||||
<div class="col-sm-6">
|
||||
<input type="password"
|
||||
class="form-control myValue"
|
||||
id="confimNewPassword"
|
||||
placeholder=" Confirmation nouveau mot de passe"
|
||||
[(ngModel)]="confirmNewPassword"
|
||||
[disabled]="!changePassword">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
||||
<!-- ----------------------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- Colonne droite -->
|
||||
<ng-template #rightCol>
|
||||
<div class="col-6" style="border-left: solid 1px #e6e6e6">
|
||||
|
||||
<!-- interests -->
|
||||
<div class="form-group row myRow">
|
||||
<label for="interests" class="col-sm-3 col-form-label myLeftLabel">Centres d'intérêt:</label>
|
||||
<div class="col-sm-9">
|
||||
<app-input-interests-profil
|
||||
id="interests"
|
||||
[myInterests]="userCopy.interests"
|
||||
(eventEmitter)="onEventInputInterests($event)"></app-input-interests-profil>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
.myContainer {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0px 0px 10px 0px;
|
||||
width: 5vw;
|
||||
height: 5vw;
|
||||
border: solid 2px black;
|
||||
border-radius: 50%;
|
||||
font-size: xxx-large;
|
||||
}
|
||||
|
||||
.inputUrlImage {
|
||||
width: 80%;
|
||||
text-align: center;
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
font-size: small;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
.myRow {
|
||||
margin: 15px 0px 15px 0px;
|
||||
}
|
||||
.myLeftLabel {
|
||||
text-align: right;
|
||||
padding: 0px 0px 0px 0px;
|
||||
margin: 0px 0px 0px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.myRightLabel {
|
||||
text-align: left;
|
||||
padding: 0px 0px 0px 0px;
|
||||
margin: 0px 0px 0px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.myValue {
|
||||
text-align: left;
|
||||
padding: 0px 0px 0px 0px;
|
||||
margin: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// aura
|
||||
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||
background-color: grey !important;
|
||||
}
|
||||
|
||||
// contenu coche
|
||||
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
// indeterminate
|
||||
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
::ng-deep .mat-radio-inner-circle {
|
||||
color: black !important;
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
::ng-deep .mat-radio-outer-circle{
|
||||
color: black !important;
|
||||
border: solid 1px gray !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupUpdateUserComponent } from './popup-update-user.component';
|
||||
|
||||
describe('PopupUpdateUserComponent', () => {
|
||||
let component: PopupUpdateUserComponent;
|
||||
let fixture: ComponentFixture<PopupUpdateUserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupUpdateUserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupUpdateUserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-update-user',
|
||||
templateUrl: './popup-update-user.component.html',
|
||||
styleUrls: ['./popup-update-user.component.scss']
|
||||
})
|
||||
export class PopupUpdateUserComponent implements OnInit
|
||||
{
|
||||
userCopy;
|
||||
newPassword: string = "";
|
||||
confirmNewPassword: string = "" ;
|
||||
changePassword: boolean = false ;
|
||||
hasError: boolean = false;
|
||||
errorMessage: string = "" ;
|
||||
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupUpdateUserComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
private messageService: MessageService,
|
||||
private profilService: ProfilService ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
const user0 = this.data.user;
|
||||
this.userCopy = {
|
||||
_id: user0._id,
|
||||
login: user0.login,
|
||||
hashPass: user0.hashPass,
|
||||
email: user0.email,
|
||||
role: {
|
||||
name: user0.role.name,
|
||||
permission: user0.role.permission,
|
||||
isAccepted: user0.role.isAccepted,
|
||||
},
|
||||
profileImageUrl: user0.profileImageUrl,
|
||||
dateOfBirth: user0.dateOfBirth,
|
||||
gender: user0.gender,
|
||||
interests: [],
|
||||
company: "",
|
||||
isActive: user0.isActive,
|
||||
createdAt: user0.createdAt,
|
||||
updatedAt: user0.updatedAt,
|
||||
lastConnexion: new Date()
|
||||
};
|
||||
for(let interest of user0.interests) this.userCopy.interests.push(interest);
|
||||
}
|
||||
|
||||
|
||||
onValider()
|
||||
{
|
||||
this.checkField();
|
||||
if(!this.hasError)
|
||||
{
|
||||
if(this.changePassword) this.userCopy.hashPass = this.newPassword;
|
||||
const data = {
|
||||
login: this.userCopy.login,
|
||||
hashPass: this.userCopy.hashPass,
|
||||
email: this.userCopy.email,
|
||||
profileImageUrl: this.userCopy.profileImageUrl,
|
||||
dateOfBirth: this.userCopy.dateOfBirth,
|
||||
gender: this.userCopy.gender,
|
||||
interests: this.userCopy.interests,
|
||||
};
|
||||
this.messageService
|
||||
.put("user/update/"+this.profilService.getId(), data)
|
||||
.subscribe( ret => this.onValiderCallback(ret), err => this.onValiderCallback(err) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onValiderCallback(retour: any)
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
else {
|
||||
this.profilService.setProfileImageUrl(this.userCopy.profileImageUrl);
|
||||
this.dialogRef.close(this.userCopy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
checkField()
|
||||
{
|
||||
if(this.userCopy.login.length === 0) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'pseudo'." ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.userCopy.email.length === 0) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'email'." ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(!this.isValidEmail(this.userCopy.email)) {
|
||||
this.errorMessage = "Email invalide." ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if((this.changePassword) && (this.newPassword.length === 0)) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'mot de passe'" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if((this.changePassword) && (this.newPassword !== this.confirmNewPassword)) {
|
||||
this.errorMessage = "Le mot de passe est différent de sa confirmation" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.errorMessage = "" ;
|
||||
this.hasError = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isValidEmail(email)
|
||||
{
|
||||
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
|
||||
onEventInputInterests(myInterets: string[])
|
||||
{
|
||||
this.userCopy.interests = myInterets;
|
||||
}
|
||||
|
||||
}
|
||||
80
src/app/user/search/page-search/page-search.component.html
Normal file
80
src/app/user/search/page-search/page-search.component.html
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- Navbar -->
|
||||
<div style="margin-bottom: 30px">
|
||||
<app-navbar-user></app-navbar-user>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- --------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- [Search bar] + [Sites de streaming] -->
|
||||
<div style="width: 100%; margin: 0 auto;">
|
||||
|
||||
<!-- Search bar -->
|
||||
<div class="input-group" style="width: 100%; margin: 0 auto;">
|
||||
<div class="form-outline" style="width: 100%; margin: 0 auto;">
|
||||
<input type="search" placeholder="Rechercher..." class="inputSearchBar" [(ngModel)]="search" (keydown)="onEnterOnSearchBar($event)"/>
|
||||
<button mat-icon-button (click)="onSearch()">
|
||||
<mat-icon>search</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sites de streaming -->
|
||||
<div style="margin-bottom: 20px">
|
||||
<!-- youtube -->
|
||||
<span>
|
||||
|
||||
<mat-checkbox id="youtube" name="youtube" style="margin-left: 5px" [(ngModel)]="tabPlateform[0].isSelected"></mat-checkbox>
|
||||
<img src="/assets/logo_plateforms/youtube.png" alt="logo" width="30px" height="25px" style="margin-left: 5px">
|
||||
<label for="youtube" style="margin-left: 5px">Youtube</label>
|
||||
|
||||
</span>
|
||||
<!-- dailymotion -->
|
||||
<span>
|
||||
|
||||
<mat-checkbox id="dailymotion" name="dailymotion" style="margin-left: 5px" [(ngModel)]="tabPlateform[1].isSelected"></mat-checkbox>
|
||||
<img src="/assets/logo_plateforms/dailymotion.png" alt="logo" width="25px" height="25px" style="margin-left: 5px">
|
||||
<label for="dailymotion" style="margin-left: 5px">Dailymotion</label>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- --------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- [pub gauche] + [Grilles des videos] + [pub droite] -->
|
||||
<mat-grid-list cols="11" rowHeight="75vh">
|
||||
|
||||
<!-- pub gauche -->
|
||||
<mat-grid-tile colspan="2" rowspan="1" class="cellulePub">
|
||||
<div class="conteneurPub">
|
||||
<app-advert [ad]="ad1"></app-advert>
|
||||
</div>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- Grilles des videos -->
|
||||
<mat-grid-tile colspan="7" rowspan="1" class="celluleGrilleVideo">
|
||||
<div class="conteneurVideosGrid">
|
||||
<app-video-grid [tabVideo]="tabVideo" [search]="search" [sources]="sources" [indexPage]="indexPage"></app-video-grid>
|
||||
</div>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- pub droite -->
|
||||
<mat-grid-tile colspan="2" rowspan="1" class="cellulePub">
|
||||
<div class="conteneurPub">
|
||||
<app-advert [ad]="ad1"></app-advert>
|
||||
</div>
|
||||
</mat-grid-tile>
|
||||
|
||||
</mat-grid-list>
|
||||
<br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
90
src/app/user/search/page-search/page-search.component.scss
Normal file
90
src/app/user/search/page-search/page-search.component.scss
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
.lightTheme {
|
||||
color: black;
|
||||
border-color: black;
|
||||
}
|
||||
.darkTheme {
|
||||
color: white;
|
||||
border-color: white;
|
||||
}
|
||||
.myContainer {
|
||||
text-align: center;
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
.inputSearchBar {
|
||||
width: 40%;
|
||||
font-size: large;
|
||||
border-bottom: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
.celluleGrilleVideo {
|
||||
border: solid 2px;
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
.lightTheme .celluleGrilleVideo{
|
||||
border-color: black;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
.darkTheme .celluleGrilleVideo{
|
||||
border-color: white;
|
||||
background-color: #646464;
|
||||
}
|
||||
|
||||
.conteneurVideosGrid {
|
||||
height: 75vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
.cellulePub {
|
||||
padding: 0px 10px 0px 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.conteneurPub {
|
||||
height: 75vh;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
width: 75%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
// aura
|
||||
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||
background-color: grey !important;
|
||||
}
|
||||
|
||||
// contenu coche
|
||||
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
// indeterminate
|
||||
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||
border: solid 1px black !important;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageSearchComponent } from './page-search.component';
|
||||
|
||||
describe('PageSearchComponent', () => {
|
||||
let component: PageSearchComponent;
|
||||
let fixture: ComponentFixture<PageSearchComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageSearchComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageSearchComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
118
src/app/user/search/page-search/page-search.component.ts
Normal file
118
src/app/user/search/page-search/page-search.component.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {HttpParams} from "@angular/common/http";
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
|
||||
|
||||
|
||||
let TAB_PLATEFORM = [
|
||||
{ name: "Youtube", isSelected: true },
|
||||
{ name: "Dailymotion", isSelected: true }
|
||||
];
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-search',
|
||||
templateUrl: './page-search.component.html',
|
||||
styleUrls: ['./page-search.component.scss']
|
||||
})
|
||||
export class PageSearchComponent implements OnInit
|
||||
{
|
||||
tabPlateform = TAB_PLATEFORM;
|
||||
tabVideo: any[] = [];
|
||||
search: string = "";
|
||||
ad1: any;
|
||||
ad2: any;
|
||||
sources: string = "" ;
|
||||
indexPage: number = 0;
|
||||
|
||||
|
||||
constructor( private messageService: MessageService,
|
||||
public themeService: ThemeService,
|
||||
private activatedRoute: ActivatedRoute ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
// parametre de la route
|
||||
this.activatedRoute
|
||||
.queryParams
|
||||
.subscribe(paramsFromOldPage => {
|
||||
if(paramsFromOldPage.hasOwnProperty("search")) this.search = paramsFromOldPage.search;
|
||||
if(paramsFromOldPage.hasOwnProperty("sources"))
|
||||
{
|
||||
this.sources = paramsFromOldPage.sources;
|
||||
if(this.sources === "yt") {
|
||||
this.tabPlateform[0].isSelected = true;
|
||||
this.tabPlateform[1].isSelected = false;
|
||||
}
|
||||
else if(this.sources === "dm") {
|
||||
this.tabPlateform[0].isSelected = false;
|
||||
this.tabPlateform[1].isSelected = true;
|
||||
}
|
||||
else if(this.sources === "yt,dm") {
|
||||
this.tabPlateform[0].isSelected = true;
|
||||
this.tabPlateform[1].isSelected = true;
|
||||
}
|
||||
}
|
||||
if(paramsFromOldPage.hasOwnProperty("indexPage")) this.indexPage = parseInt(paramsFromOldPage.indexPage, 10);
|
||||
this.onSearch();
|
||||
});
|
||||
|
||||
// Ask for ads
|
||||
let params = new HttpParams();
|
||||
params = params.append("quantity", 2);
|
||||
this.messageService
|
||||
.get("user/ad", params)
|
||||
.subscribe(ret => this.adCallback(ret), err => this.adCallback(err));
|
||||
}
|
||||
|
||||
|
||||
adCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.ad1 = retour.data[0];
|
||||
this.ad2 = retour.data[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onSearch()
|
||||
{
|
||||
let params = new HttpParams();
|
||||
params = params.append('q', this.search);
|
||||
|
||||
if(this.tabPlateform[0].isSelected && this.tabPlateform[1].isSelected) this.sources = "yt,dm" ;
|
||||
else if((!this.tabPlateform[0].isSelected) && this.tabPlateform[1].isSelected) this.sources = "dm" ;
|
||||
else if(this.tabPlateform[0].isSelected && (!this.tabPlateform[1].isSelected)) this.sources = "yt" ;
|
||||
else this.sources = "" ;
|
||||
params = params.append('sources', this.sources);
|
||||
|
||||
this.messageService
|
||||
.get("video/search", params)
|
||||
.subscribe(ret => this.onSearchCallback(ret), err => this.onSearchCallback(err));
|
||||
}
|
||||
|
||||
|
||||
onSearchCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.tabVideo = retour.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onEnterOnSearchBar(event)
|
||||
{
|
||||
if(event.key === 'Enter') this.onSearch();
|
||||
}
|
||||
|
||||
}
|
||||
76
src/app/user/search/video-grid/video-grid.component.html
Normal file
76
src/app/user/search/video-grid/video-grid.component.html
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- Grille -->
|
||||
<div style="height: 93%; background-color: white; padding-top: 7px;" *ngIf="tabVideoExists()">
|
||||
<mat-grid-list cols="3" rowHeight="33%">
|
||||
<mat-grid-tile colspan="1" rowspan="1" *ngFor="let k of [0,1,2,3,4,5,6,7,8]">
|
||||
<div class="myCell" *ngIf="indexPage+k < tabVideo.length" [title]="tabVideo[indexPage+k].title">
|
||||
|
||||
|
||||
<!-- Image video -->
|
||||
<div class="imgsContainer">
|
||||
<img class="imgPlay" src="/assets/play.png" (click)="onVideo(tabVideo[indexPage+k])">
|
||||
<img class="imgVideo" [src]="tabVideo[indexPage+k].imageUrl" (click)="onVideo(tabVideo[indexPage+k])">
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Info video -->
|
||||
<mat-grid-list cols="12" class="mat-grid-list-info-video">
|
||||
|
||||
<!-- logo -->
|
||||
<mat-grid-tile [colspan]="2" [rowspan]="2" class="mat-grid-tile-info-video" (click)="onVideo(tabVideo[indexPage+k])">
|
||||
<img *ngIf="tabVideo[indexPage+k].source==='Youtube'" src="/assets/logo_plateforms/youtube.png" alt="ytb" width="20px" height="15px">
|
||||
<img *ngIf="tabVideo[indexPage+k].source==='Dailymotion'" src="/assets/logo_plateforms/dailymotion.png" alt="dlm" width="20px" height="20px">
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- title + views + publishedAt -->
|
||||
<mat-grid-tile [colspan]="8" [rowspan]="2" class="mat-grid-tile-info-video" (click)="onVideo(tabVideo[indexPage+k])">
|
||||
<div style="position: absolute; left: 1px; text-align: left">
|
||||
{{tronquage(tabVideo[indexPage+k].title)}}
|
||||
<br>
|
||||
<span style="color: gray">
|
||||
{{tabVideo[indexPage+k].views | number: '1.0-0'}} vues.
|
||||
Il y a {{dateToElapsedTime(tabVideo[indexPage+k].publishedAt)}}.
|
||||
</span>
|
||||
</div>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- addButton -->
|
||||
<mat-grid-tile [colspan]="2" [rowspan]="2" class="mat-grid-tile-info-video">
|
||||
<button mat-icon-button (click)="onAddToPlaylist(tabVideo[indexPage+k])">
|
||||
<mat-icon>add_circle</mat-icon>
|
||||
</button>
|
||||
</mat-grid-tile>
|
||||
|
||||
</mat-grid-list>
|
||||
|
||||
|
||||
</div>
|
||||
</mat-grid-tile>
|
||||
</mat-grid-list>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Paginator -->
|
||||
<div class="paginatorContainer" *ngIf="tabVideoExists()">
|
||||
|
||||
<!-- btn précédent -->
|
||||
<button mat-button class="btnPaginator" [disabled]="indexPage<=0" (click)="indexPage=indexPage-9"> < Précédent </button>
|
||||
|
||||
<!-- numeros de page -->
|
||||
<span *ngFor="let page of tabPage" (click)="indexPage=(page-1)*9">
|
||||
<span *ngIf="page===((indexPage/9)+1)">
|
||||
<span style="text-decoration: underline; cursor: pointer;font-weight: bold;">{{page}}</span>
|
||||
</span>
|
||||
<span *ngIf="page!==((indexPage/9)+1)">
|
||||
<span style="text-decoration: underline; cursor: pointer;">{{page}}</span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- btn suivant -->
|
||||
<button mat-button class="btnPaginator" [disabled]="indexPage+9>=tabVideo.length" (click)="indexPage=indexPage+9"> Suivant > </button>
|
||||
|
||||
</div>
|
||||
84
src/app/user/search/video-grid/video-grid.component.scss
Normal file
84
src/app/user/search/video-grid/video-grid.component.scss
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
mat-grid-list {
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
mat-grid-tile {
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.myCell {
|
||||
margin: 7px 0px 0px 0px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
background-color: white;
|
||||
border: solid 1px black;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.myCell:hover {
|
||||
background-color: #d2d2d2;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
.imgsContainer {
|
||||
//width: 20vw;
|
||||
width: 18vw;
|
||||
height: 15vh;
|
||||
}
|
||||
|
||||
.imgPlay {
|
||||
position: absolute;
|
||||
margin-left: 8vw;
|
||||
width: 2.5vw;
|
||||
margin-top: 5vh;
|
||||
height: 5vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.imgVideo {
|
||||
width: 18vw;
|
||||
height: 15vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
.mat-grid-list-info-video {
|
||||
margin: 0px 0px 0px 0px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.mat-grid-tile-info-video {
|
||||
border: none;
|
||||
font-size: x-small;
|
||||
//background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
//background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
.paginatorContainer {
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 5px 0px 0px 0px;
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.btnPaginator {
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
25
src/app/user/search/video-grid/video-grid.component.spec.ts
Normal file
25
src/app/user/search/video-grid/video-grid.component.spec.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { VideoGridComponent } from './video-grid.component';
|
||||
|
||||
describe('VideoGridComponent', () => {
|
||||
let component: VideoGridComponent;
|
||||
let fixture: ComponentFixture<VideoGridComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ VideoGridComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(VideoGridComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
128
src/app/user/search/video-grid/video-grid.component.ts
Normal file
128
src/app/user/search/video-grid/video-grid.component.ts
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
|
||||
import {AddVideoToPlaylistsService} from "../../utils/services/addVideoToPlaylists/add-video-to-playlists.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-video-grid',
|
||||
templateUrl: './video-grid.component.html',
|
||||
styleUrls: ['./video-grid.component.scss']
|
||||
})
|
||||
export class VideoGridComponent implements OnChanges
|
||||
{
|
||||
@Input() tabVideo: any[] = [];
|
||||
@Input() search: string = "";
|
||||
@Input() sources: string = "";
|
||||
@Input() indexPage: number = 0;
|
||||
tabPage: number[] = [];
|
||||
|
||||
|
||||
constructor( private addVideoToPlaylistsService: AddVideoToPlaylistsService,
|
||||
private router: Router,
|
||||
private messageService: MessageService ) {}
|
||||
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void
|
||||
{
|
||||
if(this.tabVideoExists())
|
||||
{
|
||||
const nbVideo = this.tabVideo.length;
|
||||
let nbPage = Math.floor(nbVideo/9);
|
||||
if((nbVideo%9) !== 0) nbPage += 1;
|
||||
this.tabPage = [];
|
||||
for(let i=1 ; i<=nbPage ; i++) this.tabPage.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onAddToPlaylist(video): void
|
||||
{
|
||||
this.addVideoToPlaylistsService.run(video.videoId, video.source, video.interest);
|
||||
}
|
||||
|
||||
|
||||
tronquage(str: string)
|
||||
{
|
||||
if(str.length < 30) return str;
|
||||
else return str.substring(0, 27) + "..." ;
|
||||
}
|
||||
|
||||
|
||||
onVideo(video): void
|
||||
{
|
||||
const data = { source: video.source, interest: video.interest };
|
||||
this.messageService
|
||||
.post("video/create/"+video.videoId, data)
|
||||
.subscribe(ret => this.onVideoCallback(ret,video), err => this.onVideoCallback(err,video));
|
||||
}
|
||||
|
||||
|
||||
onVideoCallback(retour: any, video): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
const params = {
|
||||
videoId: video.videoId,
|
||||
source: video.source,
|
||||
from: "search",
|
||||
search: this.search,
|
||||
sources: this.sources,
|
||||
indexPage: this.indexPage
|
||||
};
|
||||
this.router.navigate(['/user/watching'], { queryParams: params });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dateToElapsedTime(date0): string
|
||||
{
|
||||
const ellapsedTimeInMilliSeconds = (new Date()).getTime() - (new Date(date0)).getTime();
|
||||
|
||||
// seconde
|
||||
const ellapsedTimeInSeconds = Math.trunc(ellapsedTimeInMilliSeconds / 1000);
|
||||
if(ellapsedTimeInSeconds < 60) {
|
||||
if(ellapsedTimeInSeconds <= 1)return ellapsedTimeInSeconds + " seconde" ;
|
||||
else return ellapsedTimeInSeconds + " secondes" ;
|
||||
}
|
||||
// minute
|
||||
const ellapsedTimeInMinutes = Math.trunc(ellapsedTimeInSeconds / 60);
|
||||
if(ellapsedTimeInMinutes < 60) {
|
||||
if(ellapsedTimeInMinutes <= 1) return ellapsedTimeInMinutes + " minute" ;
|
||||
else return ellapsedTimeInMinutes + " minutes" ;
|
||||
}
|
||||
// heure
|
||||
const ellapsedTimeInHours = Math.trunc(ellapsedTimeInMinutes / 60);
|
||||
if(ellapsedTimeInHours < 24) {
|
||||
if(ellapsedTimeInHours <= 1) return ellapsedTimeInHours + " heure" ;
|
||||
else return ellapsedTimeInHours + " heures" ;
|
||||
}
|
||||
// jour
|
||||
const ellapsedTimeInDays = Math.trunc(ellapsedTimeInHours / 24);
|
||||
if(ellapsedTimeInDays < 31) {
|
||||
if(ellapsedTimeInDays <= 1) return ellapsedTimeInDays + " jour" ;
|
||||
else return ellapsedTimeInDays + " jours" ;
|
||||
}
|
||||
// mois
|
||||
const ellapsedTimeInMonths = Math.trunc(ellapsedTimeInDays / 31);
|
||||
if(ellapsedTimeInMonths < 12) {
|
||||
return ellapsedTimeInMonths + " mois" ;
|
||||
}
|
||||
// an
|
||||
const ellapsedTimeInYears = Math.trunc(ellapsedTimeInMonths / 12);
|
||||
if(ellapsedTimeInYears <= 1) return ellapsedTimeInYears + " an" ;
|
||||
else return ellapsedTimeInYears + " ans" ;
|
||||
}
|
||||
|
||||
|
||||
tabVideoExists(): boolean
|
||||
{
|
||||
if((this.tabVideo === null) || (this.tabVideo === undefined)) return false;
|
||||
else if(this.tabVideo.length === 0) return false;
|
||||
else return true;
|
||||
}
|
||||
|
||||
}
|
||||
26
src/app/user/utils/components/advert/advert.component.html
Normal file
26
src/app/user/utils/components/advert/advert.component.html
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<div *ngIf="(from==='search' || from==='myPlaylists') && (imageExist)"
|
||||
class="myContainer">
|
||||
|
||||
<span class="helper"></span>
|
||||
<img [src]="image.base64"
|
||||
[alt]="image.description"
|
||||
(click)="onClick()"
|
||||
id="imgFromSearchOrMyPlaylists">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- --------------------------------------------------------------------- -->
|
||||
|
||||
<img *ngIf="from === 'watchingLeft' && (imageExist)"
|
||||
[src]="image.base64"
|
||||
[alt]="image.description"
|
||||
(click)="onClick()"
|
||||
id="imgFromWatchingLeft">
|
||||
|
||||
<!-- --------------------------------------------------------------------- -->
|
||||
|
||||
<img *ngIf="from === 'watchingRight' && (imageExist)"
|
||||
[src]="image.base64"
|
||||
[alt]="image.description"
|
||||
(click)="onClick()"
|
||||
id="imgFromWatchingRight">
|
||||
41
src/app/user/utils/components/advert/advert.component.scss
Normal file
41
src/app/user/utils/components/advert/advert.component.scss
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
.myContainer {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
#imgFromSearchOrMyPlaylists {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
border: solid 3px black;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
#imgFromWatchingLeft {
|
||||
width: 14vw;
|
||||
height: 70vh;
|
||||
border: solid 3px black;
|
||||
position: fixed;
|
||||
left: 1vw;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#imgFromWatchingRight {
|
||||
width: 15vw;
|
||||
height: 70vh;
|
||||
border: solid 3px black;
|
||||
position: fixed;
|
||||
right: 1vw;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdvertComponent } from './advert.component';
|
||||
|
||||
describe('PubComponent', () => {
|
||||
let component: AdvertComponent;
|
||||
let fixture: ComponentFixture<AdvertComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AdvertComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AdvertComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
40
src/app/user/utils/components/advert/advert.component.ts
Normal file
40
src/app/user/utils/components/advert/advert.component.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-advert',
|
||||
templateUrl: './advert.component.html',
|
||||
styleUrls: ['./advert.component.scss']
|
||||
})
|
||||
export class AdvertComponent implements OnChanges
|
||||
{
|
||||
@Input() ad: any;
|
||||
@Input() from: string = "search";
|
||||
image: any;
|
||||
imageExist: boolean = false;
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void
|
||||
{
|
||||
if((this.ad !== null) && (this.ad !== undefined))
|
||||
{
|
||||
const nbImages = this.ad.images.length;
|
||||
const indexImage = Math.floor(Math.random() * nbImages);
|
||||
this.image = this.ad.images[indexImage];
|
||||
this.imageExist = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onClick(): void
|
||||
{
|
||||
if((this.ad.url !== "") && (this.ad.url !== null) && (this.ad.url !== undefined)) {
|
||||
document.location.href = this.ad.url;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<nav class="navbar navbar-expand-lg">
|
||||
|
||||
<!-- PolyNotFound -->
|
||||
<a class="navbar-brand" routerLink="/user/search"> StreamNotFound </a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
|
||||
<!-- [Recherche] [Mes Playlists] [Historique] -->
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item active monLi">
|
||||
<a *ngIf="(url !== routes[0]) && (url !== routes[1])" [routerLink]="routes[1]" class="nav-link">Rechercher</a>
|
||||
<a *ngIf="(url === routes[0]) || (url === routes[1])" [routerLink]="routes[1]" class="nav-link myActiveLink">Rechercher</a>
|
||||
</li>
|
||||
<li class="nav-item active monLi">
|
||||
<a *ngIf="url !== routes[2]" [routerLink]="routes[2]" class="nav-link">Mes playlists</a>
|
||||
<a *ngIf="url === routes[2]" [routerLink]="routes[2]" class="nav-link myActiveLink">Mes playlists</a>
|
||||
</li>
|
||||
<li class="nav-item active monLi">
|
||||
<a *ngIf="url !== routes[3]" [routerLink]="routes[3]" class="nav-link">Historique</a>
|
||||
<a *ngIf="url === routes[3]" [routerLink]="routes[3]" class="nav-link myActiveLink">Historique</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Mon profil -->
|
||||
<img [src]=profilService.getProfileImageUrl()
|
||||
onerror="this.onerror=null; this.src='assets/profil.png'"
|
||||
[routerLink]="routes[4]"
|
||||
alt="">
|
||||
|
||||
|
||||
<!-- Deconnexion -->
|
||||
<button mat-button class="btnDeconnexion" (click)="onDeconnexion()" routerLink="/">
|
||||
Déconnexion
|
||||
</button>
|
||||
|
||||
</nav>
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
.navbar {
|
||||
background-color: black;
|
||||
height: 60px;
|
||||
font-size: medium;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.navbar-expand-lg {
|
||||
border-bottom: solid;
|
||||
border-color: white;
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
|
||||
|
||||
// PolyNotFound
|
||||
.navbar-brand {
|
||||
font-family: cursive;
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
margin-left: 15px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.monLi {
|
||||
margin: 0px 10px 0px 10px;
|
||||
}
|
||||
|
||||
|
||||
.nav-link {
|
||||
color: white;
|
||||
}
|
||||
.nav-link:hover {
|
||||
color: grey;
|
||||
}
|
||||
.myActiveLink {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
.btnDeconnexion {
|
||||
font-size: medium;
|
||||
margin: 0px 10px 0px 10px
|
||||
}
|
||||
.btnDeconnexion:hover {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
border: solid 2px white;
|
||||
border-radius: 50px;
|
||||
margin: 0px 10px 0px 15px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
img:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
||||
::ng-deep .mat-slide-toggle-thumb {
|
||||
background-color: #c8c8c8;
|
||||
}
|
||||
|
||||
::ng-deep .mat-slide-toggle-bar {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
|
||||
background-color: #646464;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NavbarUserComponent } from './navbar-user.component';
|
||||
|
||||
describe('NavbarUserComponent', () => {
|
||||
let component: NavbarUserComponent;
|
||||
let fixture: ComponentFixture<NavbarUserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ NavbarUserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NavbarUserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {Router} from "@angular/router";
|
||||
import {ProfilService} from "../../../../utils/profil/profil.service";
|
||||
import {MessageService} from "../../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar-user',
|
||||
templateUrl: './navbar-user.component.html',
|
||||
styleUrls: ['./navbar-user.component.scss']
|
||||
})
|
||||
export class NavbarUserComponent
|
||||
{
|
||||
routes: string[] = [
|
||||
"/user", // 0
|
||||
"/user/search", // 1
|
||||
"/user/myPlaylists", // 2
|
||||
"/user/history", // 3
|
||||
"/user/myProfil" // 4
|
||||
];
|
||||
|
||||
url = this.router.url;
|
||||
|
||||
constructor( private router: Router,
|
||||
public profilService: ProfilService,
|
||||
private messageService: MessageService ) { }
|
||||
|
||||
onDeconnexion(): void
|
||||
{
|
||||
this.messageService
|
||||
.delete('user/logout')
|
||||
.subscribe(retour => this.onDeconnexionCallback(retour), err => this.onDeconnexionCallback(err));
|
||||
}
|
||||
|
||||
onDeconnexionCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") console.log(retour);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<h3> Ajouter dans </h3>
|
||||
|
||||
|
||||
<mat-divider></mat-divider><!------------------------------------------------------------------------------------------------------------->
|
||||
|
||||
|
||||
<div class="conteneurPlaylists">
|
||||
<div *ngFor="let playlist of tabPlaylistAndBool" style="margin-left: 10px">
|
||||
<mat-checkbox [(ngModel)]="playlist.isSelected"> {{playlist.name}} </mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<mat-divider></mat-divider><!------------------------------------------------------------------------------------------------------------->
|
||||
|
||||
|
||||
<div class="conteneurBtnCreerPlaylist" *ngIf="!goToCreatePlaylist">
|
||||
<button mat-button (click)="goToCreatePlaylist=true">
|
||||
<mat-label>Creer une playlist</mat-label>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="conteneurInputNewPlaylist" *ngIf="goToCreatePlaylist">
|
||||
<mat-form-field>
|
||||
<mat-label> Nom playlist </mat-label>
|
||||
<input matInput type="text" [(ngModel)]="newPlaylistName">
|
||||
</mat-form-field>
|
||||
<button mat-icon-button (click)="goToCreatePlaylist=false">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<mat-divider></mat-divider><!------------------------------------------------------------------------------------------------------------->
|
||||
|
||||
|
||||
<span *ngIf="hasError" class="mat-error"> {{errorMessage}} </span>
|
||||
<mat-dialog-actions style="justify-content: flex-end;">
|
||||
<button mat-button (click)="onAnnuler()">Annuler</button>
|
||||
<button mat-button cdkFocusInitial (click)="onValider()">Valider</button>
|
||||
</mat-dialog-actions>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
h3 {
|
||||
text-align: center;
|
||||
margin-bottom: 10px
|
||||
}
|
||||
|
||||
.conteneurPlaylists {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.conteneurBtnCreerPlaylist {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.conteneurInputNewPlaylist {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
// aura
|
||||
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||
background-color: grey !important;
|
||||
}
|
||||
|
||||
// contenu coche
|
||||
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
// indeterminate
|
||||
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||
border-color: black !important;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupAddVideoToPlaylistsComponent } from './popup-add-video-to-playlists.component';
|
||||
|
||||
describe('PopupAddVideoToPlaylistsComponent', () => {
|
||||
let component: PopupAddVideoToPlaylistsComponent;
|
||||
let fixture: ComponentFixture<PopupAddVideoToPlaylistsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupAddVideoToPlaylistsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupAddVideoToPlaylistsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {MessageService} from "../../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-add-video-to-playlists',
|
||||
templateUrl: './popup-add-video-to-playlists.component.html',
|
||||
styleUrls: ['./popup-add-video-to-playlists.component.scss']
|
||||
})
|
||||
export class PopupAddVideoToPlaylistsComponent implements OnInit
|
||||
{
|
||||
_idVideo: string = "";
|
||||
videoId: string = "";
|
||||
source: string = "";
|
||||
interest: string = "";
|
||||
|
||||
tabPlaylistAndBool = [];
|
||||
|
||||
goToCreatePlaylist = false;
|
||||
newPlaylistName = "";
|
||||
hasError: boolean = false;
|
||||
tabNomPlaylist: string[] = [];
|
||||
errorMessage: string = "" ;
|
||||
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupAddVideoToPlaylistsComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
private messageService: MessageService) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this._idVideo = this.data._idVideo;
|
||||
this.videoId = this.data.videoId;
|
||||
this.source = this.data.source;
|
||||
this.interest = this.data.interest;
|
||||
|
||||
for(let playlist of this.data.playlists)
|
||||
{
|
||||
if(playlist.videoIds.includes(this._idVideo)) playlist["isSelected"] = true;
|
||||
else playlist["isSelected"] = false;
|
||||
this.tabPlaylistAndBool.push(playlist);
|
||||
this.tabNomPlaylist.push(playlist.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onValider(): void
|
||||
{
|
||||
this.checkError();
|
||||
if(!this.hasError)
|
||||
{
|
||||
// --- Existing playlists ---
|
||||
let listeDesPlaylistsSelected = "" ;
|
||||
let listeDesPlaylistsNotSelected = "" ;
|
||||
for(let playlist of this.tabPlaylistAndBool)
|
||||
{
|
||||
if(playlist.isSelected) listeDesPlaylistsSelected += playlist.id + "," ;
|
||||
else listeDesPlaylistsNotSelected += playlist.id + "," ;
|
||||
}
|
||||
if(listeDesPlaylistsSelected.endsWith(",")) listeDesPlaylistsSelected = listeDesPlaylistsSelected.slice(0, listeDesPlaylistsSelected.length-1);
|
||||
if(listeDesPlaylistsNotSelected.endsWith(",")) listeDesPlaylistsNotSelected = listeDesPlaylistsNotSelected.slice(0, listeDesPlaylistsNotSelected.length-1);
|
||||
|
||||
if(listeDesPlaylistsSelected !== "")
|
||||
{
|
||||
const data1 = { videoId: { id: this._idVideo, action: "add" } };
|
||||
this.messageService
|
||||
.put( "playlist/update/"+listeDesPlaylistsSelected, data1)
|
||||
.subscribe( ret => this.callbackForExistingPlaylists(ret), err => this.callbackForExistingPlaylists(err));
|
||||
}
|
||||
if(listeDesPlaylistsNotSelected !== "")
|
||||
{
|
||||
const data2 = { videoId: { id: this._idVideo, action: "delete" } };
|
||||
this.messageService
|
||||
.put( "playlist/update/"+listeDesPlaylistsNotSelected, data2)
|
||||
.subscribe( ret => this.callbackForExistingPlaylists(ret), err => this.callbackForExistingPlaylists(err));
|
||||
}
|
||||
|
||||
|
||||
// --- New playlists ---
|
||||
if(this.goToCreatePlaylist)
|
||||
{
|
||||
const data3 = {
|
||||
name: this.newPlaylistName,
|
||||
video: {videoId: this.videoId, interest: this.interest, source: this.source}
|
||||
};
|
||||
this.messageService
|
||||
.post("playlist/create", data3)
|
||||
.subscribe( ret => this.callbackForNewPlaylist(ret), err => this.callbackForNewPlaylist(err));
|
||||
}
|
||||
|
||||
|
||||
// --- Finalement ---
|
||||
this.dialogRef.close("success");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
callbackForExistingPlaylists(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
callbackForNewPlaylist(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onAnnuler(): void
|
||||
{
|
||||
this.dialogRef.close("annulation");
|
||||
}
|
||||
|
||||
|
||||
checkError(): void
|
||||
{
|
||||
if(this.goToCreatePlaylist && (this.newPlaylistName === "")) {
|
||||
this.errorMessage = "Le nom ne peut pas être vide" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.goToCreatePlaylist && this.tabNomPlaylist.includes(this.newPlaylistName)){
|
||||
this.errorMessage = "Ce nom est déjà utilisé" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.hasError = false;
|
||||
this.errorMessage = "" ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AddVideoToPlaylistsService } from './add-video-to-playlists.service';
|
||||
|
||||
describe('PlaylistService', () => {
|
||||
let service: AddVideoToPlaylistsService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(AddVideoToPlaylistsService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {PopupAddVideoToPlaylistsComponent} from "../../components/popup-add-video-to-playlists/popup-add-video-to-playlists.component";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {MessageService} from "../../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AddVideoToPlaylistsService
|
||||
{
|
||||
private _idVideo: string = "" ;
|
||||
private videoId: string = "" ;
|
||||
private source: string = "" ;
|
||||
private interest: string = "" ;
|
||||
|
||||
|
||||
constructor( private messageService: MessageService,
|
||||
public dialog: MatDialog,
|
||||
private snackBar: MatSnackBar ) { }
|
||||
|
||||
|
||||
run(videoId: string, source: string, interest: string): void
|
||||
{
|
||||
this.videoId = videoId;
|
||||
this.source = source;
|
||||
this.interest = interest;
|
||||
|
||||
const data = { source: this.source, interest: this.interest };
|
||||
this.messageService
|
||||
.post("video/create/"+this.videoId, data)
|
||||
.subscribe(ret => this.afterCreatingVideo(ret), err => this.afterCreatingVideo(err));
|
||||
}
|
||||
|
||||
|
||||
private afterCreatingVideo(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this._idVideo = retour.data.id;
|
||||
this.messageService
|
||||
.get('playlist/findAll')
|
||||
.subscribe( ret => this.afterReceivingPlaylists(ret), ret => this.afterReceivingPlaylists(ret) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private afterReceivingPlaylists(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else
|
||||
{
|
||||
const config = {
|
||||
width: '30%',
|
||||
data: {
|
||||
_idVideo: this._idVideo,
|
||||
videoId: this.videoId,
|
||||
source: this.source,
|
||||
interest: this.interest,
|
||||
playlists: retour.data.filter(x => x.isActive === true)
|
||||
}
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupAddVideoToPlaylistsComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe(retour => this.afterClosingDialog(retour));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private afterClosingDialog(retour): void
|
||||
{
|
||||
let message = "" ;
|
||||
switch (retour)
|
||||
{
|
||||
case "error":
|
||||
message = "Echec de l'opération ❌" ;
|
||||
break;
|
||||
case "success":
|
||||
message = "La vidéo a bien été ajoutée ✔" ;
|
||||
break;
|
||||
case "annulation":
|
||||
case null:
|
||||
case undefined:
|
||||
message = "Opération annulée" ;
|
||||
break;
|
||||
}
|
||||
const config = { duration: 1000, panelClass: "custom-class" };
|
||||
this.snackBar.open( message, "", config);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
<!-- ----------------------------------------------------------------------------------------------------------------- -->
|
||||
<!-- NIVEAU 1 -->
|
||||
|
||||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- Navbar -->
|
||||
<div style="margin-bottom: 30px">
|
||||
<app-navbar-user></app-navbar-user>
|
||||
</div>
|
||||
|
||||
<!-- --------------------------------------------------------------------- -->
|
||||
|
||||
<!-- [Search bar] + [Sites de streaming] -->
|
||||
<div style="width: 100%; margin: 0 auto;">
|
||||
|
||||
<!-- Search bar -->
|
||||
<div class="input-group" style="width: 100%; margin: 0 auto;">
|
||||
<div class="form-outline" style="width: 100%; margin: 0 auto;">
|
||||
<input type="search" placeholder="Rechercher..." class="inputSearchBar" [(ngModel)]="search" (keydown)="onEnterOnSearchBar($event)"/>
|
||||
<button mat-icon-button (click)="onSearch()">
|
||||
<mat-icon>search</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sites de streaming -->
|
||||
<div style="margin-bottom: 20px">
|
||||
<!-- youtube -->
|
||||
<span>
|
||||
|
||||
<mat-checkbox id="youtube" name="youtube" style="margin-left: 5px" [(ngModel)]="tabPlateform[0].isSelected"></mat-checkbox>
|
||||
<img src="/assets/logo_plateforms/youtube.png" alt="logo" width="30px" height="25px" style="margin-left: 5px">
|
||||
<label for="youtube" style="margin-left: 5px">Youtube</label>
|
||||
|
||||
</span>
|
||||
<!-- dailymotion -->
|
||||
<span>
|
||||
|
||||
<mat-checkbox id="dailymotion" name="dailymotion" style="margin-left: 5px" [(ngModel)]="tabPlateform[1].isSelected"></mat-checkbox>
|
||||
<img src="/assets/logo_plateforms/dailymotion.png" alt="logo" width="25px" height="25px" style="margin-left: 5px">
|
||||
<label for="dailymotion" style="margin-left: 5px">Dailymotion</label>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- --------------------------------------------------------------------- -->
|
||||
|
||||
<!-- [pub gauche] + [video] + [pub droite] -->
|
||||
<div *ngIf="from==='search' || from==='history'; then videoFromSearchOrHistory"></div>
|
||||
|
||||
<!-- [pub gauche] + [video] + [playlist] -->
|
||||
<div *ngIf="from==='myPlaylists'; then videoFromMyPlaylists"></div>
|
||||
|
||||
<!-- --------------------------------------------------------------------- -->
|
||||
|
||||
<br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- ----------------------------------------------------------------------------------------------------------------- -->
|
||||
<!-- NIVEAU 2: videoFromSearchOrHistory -->
|
||||
|
||||
<ng-template #videoFromSearchOrHistory>
|
||||
<div class="row">
|
||||
|
||||
<!-- pub gauche -->
|
||||
<div class="col-2">
|
||||
<app-advert [ad]="ad1" from="watchingLeft"></app-advert>
|
||||
</div>
|
||||
|
||||
<!-- video -->
|
||||
<div class="col-8" style="background-color: white; border: solid 1px black;">
|
||||
<div *ngIf="true then rectangleCentral"></div>
|
||||
</div>
|
||||
|
||||
<!-- pub droite -->
|
||||
<div class="col-2">
|
||||
<app-advert [ad]="ad2" from="watchingRight"></app-advert>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
||||
|
||||
<!-- ----------------------------------------------------------------------------------------------------------------- -->
|
||||
<!-- NIVEAU 2: videoFromMyPlaylists -->
|
||||
|
||||
<ng-template #videoFromMyPlaylists>
|
||||
<div class="row">
|
||||
|
||||
<!-- pub gauche -->
|
||||
<div class="col-2">
|
||||
<app-advert [ad]="ad1" from="watchingLeft"></app-advert>
|
||||
</div>
|
||||
|
||||
<!-- rectangle central -->
|
||||
<div class="col-6" style="background-color: white; border: solid 1px black;">
|
||||
<div *ngIf="true then rectangleCentral"></div>
|
||||
</div>
|
||||
|
||||
<!-- playlist -->
|
||||
<div class="col-4">
|
||||
<div *ngIf="true; then playlistHTML"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
||||
|
||||
<!-- ----------------------------------------------------------------------------------------------------------------- -->
|
||||
<!-- NIVEAU 3: rectangleCentral -->
|
||||
|
||||
<ng-template #rectangleCentral>
|
||||
|
||||
<!-- btnRetour + btnAdd -->
|
||||
<div class="row" style="margin: 10px 0px 40px 0px">
|
||||
|
||||
<!-- btnRetour -->
|
||||
<div class="col-6" style="text-align: left;">
|
||||
<button mat-button style="border: solid 1px black" (click)="onRetour()">
|
||||
<mat-icon>arrow_back_ios</mat-icon> Retour
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- btnAdd -->
|
||||
<div class="col-6" style="text-align: right;">
|
||||
<button mat-button style="border: solid 1px black" (click)="onAddToPlaylist()">
|
||||
Ajouter à une playlist <mat-icon>add_circle</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- logo + title + video + views + publishedAt + description -->
|
||||
<div [style]=containerStyle>
|
||||
|
||||
<!-- logo + title -->
|
||||
<div style="text-align: left">
|
||||
<img *ngIf="video.source==='youtube'" src="/assets/logo_plateforms/youtube.png" alt="ytb" width="50px" height="30px" style="float: left">
|
||||
<img *ngIf="video.source==='dailymotion'" src="/assets/logo_plateforms/dailymotion.png" alt="dlm" width="30px" height="30px" style="float: left">
|
||||
<h5> {{video.title}}</h5>
|
||||
</div>
|
||||
|
||||
<!-- video -->
|
||||
<div style="width: 100%">
|
||||
<iframe [src]="safeUrl(video.videoId,video.source)"
|
||||
[style]="iframeStyle"
|
||||
allowfullscreen></iframe>
|
||||
</div>
|
||||
|
||||
<!-- views -->
|
||||
<div style="text-align: left; margin: 5px 0px 0px 1px">
|
||||
<span style="font-weight: bold"> Vues: </span>
|
||||
{{video.views | number: '1.0-0'}}
|
||||
</div>
|
||||
|
||||
<!-- publishedAt -->
|
||||
<div style="text-align: left; margin: 5px 0px 0px 1px">
|
||||
<span style="font-weight: bold"> Date de publication: </span>
|
||||
{{ video.publishedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||
</div>
|
||||
|
||||
<!-- description -->
|
||||
<div style="text-align: left; margin: 5px 0px 20px 1px">
|
||||
<div style="font-weight: bold; display: flex; align-items: center; cursor: pointer" (click)="hiddenDescription = !hiddenDescription">
|
||||
Description
|
||||
<mat-icon *ngIf="hiddenDescription">expand_more</mat-icon>
|
||||
<mat-icon *ngIf="!hiddenDescription">expand_less</mat-icon>
|
||||
</div>
|
||||
<div style="margin-left: 25px; padding-left: 5px; border-left: solid 1px #e6e6e6" [hidden]="hiddenDescription">
|
||||
{{video.description}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
|
||||
|
||||
|
||||
<!-- ----------------------------------------------------------------------------------------------------------------- -->
|
||||
<!-- NIVEAU 3: playlist -->
|
||||
|
||||
<ng-template #playlistHTML>
|
||||
<div class="playlistContainer" *ngIf="playlistExists()">
|
||||
|
||||
<!-- Bordure haute -->
|
||||
<div class="topBorder">
|
||||
<span style="font-weight: bold"> {{playlist.name}} </span>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Liste des videos -->
|
||||
<div class="listVideoContainer">
|
||||
<div *ngFor="let video0 of videosInPlaylist; let i = index;">
|
||||
<div [class]="getClassOfVideoCell(video0)" (click)="this.video = video0">
|
||||
<div class="imgsContainer">
|
||||
<img class="imgPlay" src="/assets/play.png">
|
||||
<img class="imgVideo" [src]="video0.imageUrl">
|
||||
</div>
|
||||
<div class="infoContainer">
|
||||
<div class="titleContainer">{{video0.title}}</div>
|
||||
<div class="viewsContainer">
|
||||
<!-- <span>{{video.views | number: '1.0-0'}}</span> <mat-icon>visibility</mat-icon> -->
|
||||
{{video.views | number: '1.0-0'}} vues
|
||||
</div>
|
||||
<span class="publishedAtContainer">
|
||||
Publiée le {{ video.publishedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Bordure basse -->
|
||||
<!--
|
||||
<div class="bottomBorder">
|
||||
<div style="visibility: hidden">a</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
.lightTheme {
|
||||
color: black;
|
||||
border-color: black;
|
||||
}
|
||||
.darkTheme {
|
||||
color: white;
|
||||
border-color: white;
|
||||
}
|
||||
.myContainer {
|
||||
text-align: center;
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
.inputSearchBar {
|
||||
width: 40%;
|
||||
font-size: large;
|
||||
border-bottom: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
// --- Playlist ---
|
||||
|
||||
.playlistContainer {
|
||||
border: solid 1px black;
|
||||
width: 98%;
|
||||
border-top-right-radius: 10px;
|
||||
border-top-left-radius: 10px;
|
||||
}
|
||||
|
||||
.topBorder {
|
||||
margin: 0px 0px 0px 0px;
|
||||
//background-color: #dcdcdc;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
text-align: left;
|
||||
padding: 5px 0px 5px 5px;
|
||||
border-bottom: solid 1px black;
|
||||
border-top-right-radius: 10px;
|
||||
border-top-left-radius: 10px;
|
||||
}
|
||||
|
||||
.listVideoContainer {
|
||||
height: 70vh;
|
||||
background-color: white;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.videoCell {
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
border-bottom: solid 1px black;
|
||||
cursor: pointer;
|
||||
}
|
||||
.videoCell:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.videoCellFocus {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.videoCellFocus:hover {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
.imgsContainer {
|
||||
position: relative;
|
||||
width: 13vw;
|
||||
height: 10vh;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.imgVideo {
|
||||
border: solid 1px black;
|
||||
width: 13vw;
|
||||
height: 10vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.imgPlay {
|
||||
position: absolute;
|
||||
margin-left: 6vw;
|
||||
width: 2vw;
|
||||
margin-top: 3vh;
|
||||
height: 4vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
.infoContainer {
|
||||
display: table-cell;
|
||||
margin-left: 13vw;
|
||||
height: 10vh;
|
||||
padding-left: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.viewsContainer {
|
||||
text-align: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: x-small;
|
||||
color: grey;
|
||||
}
|
||||
mat-icon {
|
||||
vertical-align: middle;
|
||||
//font-size: x-small;
|
||||
}
|
||||
|
||||
.publishedAtContainer {
|
||||
text-align: left;
|
||||
font-size: x-small;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
.bottomBorder {
|
||||
margin: 0px 0px 0px 0px;
|
||||
background-color: #dcdcdc;
|
||||
border-top: solid 1px black;
|
||||
border-bottom: solid 1px black;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
// aura
|
||||
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||
background-color: grey !important;
|
||||
}
|
||||
|
||||
// contenu coche
|
||||
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
// indeterminate
|
||||
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||
border: solid 1px black !important;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageWatchingVideoComponent } from './page-watching-video.component';
|
||||
|
||||
describe('PageWatchingVideoComponent', () => {
|
||||
let component: PageWatchingVideoComponent;
|
||||
let fixture: ComponentFixture<PageWatchingVideoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageWatchingVideoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageWatchingVideoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {AddVideoToPlaylistsService} from "../../utils/services/addVideoToPlaylists/add-video-to-playlists.service";
|
||||
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
|
||||
import {HttpParams} from "@angular/common/http";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
|
||||
|
||||
|
||||
let TAB_PLATEFORM = [
|
||||
{ name: "youtube", isSelected: false },
|
||||
{ name: "dailymotion", isSelected: false }
|
||||
];
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-watching-video',
|
||||
templateUrl: './page-watching-video.component.html',
|
||||
styleUrls: ['./page-watching-video.component.scss']
|
||||
})
|
||||
export class PageWatchingVideoComponent implements OnInit
|
||||
{
|
||||
tabPlateform = TAB_PLATEFORM;
|
||||
sources: string = "";
|
||||
video = {
|
||||
title: "",
|
||||
videoId: "",
|
||||
views: 0,
|
||||
publishedAt: null,
|
||||
description: "",
|
||||
source: "",
|
||||
interest: ""
|
||||
};
|
||||
search: string = "";
|
||||
|
||||
ad1: any;
|
||||
ad2: any;
|
||||
from: string = "";
|
||||
|
||||
playlist: any;
|
||||
videosInPlaylist: any[] = [];
|
||||
|
||||
paramsFromOldPage ;
|
||||
|
||||
hiddenDescription: boolean = true;
|
||||
iframeStyle: string = "";
|
||||
containerStyle: string = "";
|
||||
|
||||
|
||||
constructor( private messageService: MessageService,
|
||||
public themeService: ThemeService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
private _sanitizer: DomSanitizer,
|
||||
private addVideoToPlaylistsService: AddVideoToPlaylistsService ) { }
|
||||
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
// Ask for videos
|
||||
this.activatedRoute
|
||||
.queryParams
|
||||
.subscribe(paramsFromOldPage => {
|
||||
|
||||
this.paramsFromOldPage = paramsFromOldPage;
|
||||
const videoId = paramsFromOldPage.videoId;
|
||||
let source = paramsFromOldPage.source;
|
||||
|
||||
let params = new HttpParams();
|
||||
if(source === "Youtube") source = "yt";
|
||||
else if(source === "Dailymotion") source = "dm" ;
|
||||
params = params.append("source", source);
|
||||
this.messageService
|
||||
.get("video/get/"+videoId, params)
|
||||
.subscribe(ret => this.findVideoCallback(ret), err => this.findVideoCallback(err));
|
||||
});
|
||||
|
||||
|
||||
// Ask for adverts
|
||||
let params = new HttpParams();
|
||||
params = params.append("quantity", 2);
|
||||
this.messageService
|
||||
.get("user/ad", params)
|
||||
.subscribe(ret => this.findAdCallback(ret), err => this.findAdCallback(err));
|
||||
|
||||
|
||||
// Si on vient de la page "search"
|
||||
if(this.router.url.includes("search"))
|
||||
{
|
||||
this.from = "search" ;
|
||||
this.activatedRoute
|
||||
.queryParams
|
||||
.subscribe(paramsFromOldPage => {
|
||||
if(paramsFromOldPage.hasOwnProperty("search")) this.search = paramsFromOldPage.search;
|
||||
if(paramsFromOldPage.hasOwnProperty("sources")) {
|
||||
this.sources = paramsFromOldPage.sources;
|
||||
if(this.sources === "yt") {
|
||||
this.tabPlateform[0].isSelected = true;
|
||||
this.tabPlateform[1].isSelected = false;
|
||||
}
|
||||
else if(this.sources === "dm") {
|
||||
this.tabPlateform[0].isSelected = false;
|
||||
this.tabPlateform[1].isSelected = true;
|
||||
}
|
||||
else if(this.sources === "yt,dm") {
|
||||
this.tabPlateform[0].isSelected = true;
|
||||
this.tabPlateform[1].isSelected = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// si on vient de la page "myPlaylists"
|
||||
else if(this.router.url.includes("myPlaylists"))
|
||||
{
|
||||
this.from = "myPlaylists";
|
||||
this.activatedRoute
|
||||
.queryParams
|
||||
.subscribe(paramsFromOldPage => {
|
||||
const _idPlaylist = paramsFromOldPage._idPlaylist;
|
||||
this.messageService
|
||||
.get("playlist/findOne/"+_idPlaylist)
|
||||
.subscribe(ret => this.afterReceivingPlaylistWithVideo(ret), err => this.afterReceivingPlaylistWithVideo(err));
|
||||
});
|
||||
|
||||
}
|
||||
// si on vient de la page "history"
|
||||
else if(this.router.url.includes("history")) this.from = "history";
|
||||
|
||||
|
||||
// style
|
||||
if(this.from === 'search' || this.from === 'history') {
|
||||
this.containerStyle = "margin: 0 auto; width: 64vw;" ;
|
||||
this.iframeStyle = "width: 64vw; height: 60vh;" ;
|
||||
}
|
||||
else {
|
||||
this.containerStyle = "margin: 0 auto; width: 48vw;" ;
|
||||
this.iframeStyle = "width: 48vw; height: 45vh;" ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
findVideoCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log("findVideoCallback: ");
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.video = retour.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
findAdCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log("findAdCallback: ");
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.ad1 = retour.data[0];
|
||||
this.ad2 = retour.data[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
afterReceivingPlaylistWithVideo(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log("afterReceivingPlaylistWithVideo");
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.playlist = retour.data;
|
||||
this.videosInPlaylist = retour.data.videos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onSearch()
|
||||
{
|
||||
if(this.tabPlateform[0].isSelected && this.tabPlateform[1].isSelected) this.sources = "yt,dm" ;
|
||||
else if((!this.tabPlateform[0].isSelected) && this.tabPlateform[1].isSelected) this.sources = "dm" ;
|
||||
else if(this.tabPlateform[0].isSelected && (!this.tabPlateform[1].isSelected)) this.sources = "yt" ;
|
||||
else this.sources = "" ;
|
||||
let options = {
|
||||
queryParams: {
|
||||
search: this.search,
|
||||
sources: this.sources,
|
||||
indexPage: 0,
|
||||
}
|
||||
};
|
||||
this.router.navigate(['/user/search'], options);
|
||||
}
|
||||
|
||||
|
||||
onAddToPlaylist(): void
|
||||
{
|
||||
this.addVideoToPlaylistsService.run(this.video.videoId, this.video.source, this.video.interest);
|
||||
}
|
||||
|
||||
|
||||
onRetour(): void
|
||||
{
|
||||
let url: string[] = [];
|
||||
let options = {};
|
||||
|
||||
if(this.from === 'search')
|
||||
{
|
||||
url = ['/user/search'];
|
||||
options = {
|
||||
queryParams: {
|
||||
search: this.paramsFromOldPage.search,
|
||||
sources: this.paramsFromOldPage.sources,
|
||||
indexPage: this.paramsFromOldPage.indexPage,
|
||||
}
|
||||
};
|
||||
}
|
||||
else if(this.from === 'myPlaylists') url = ["/user/myPlaylists"];
|
||||
else if(this.from === 'history') url = ["/user/history"];
|
||||
|
||||
this.router.navigate(url, options);
|
||||
}
|
||||
|
||||
|
||||
safeUrl(videoId: string, source: string): SafeResourceUrl
|
||||
{
|
||||
let videoUrl = "" ;
|
||||
if(source === 'Youtube') videoUrl = "https://www.youtube.com/embed/" + videoId + "?autoplay=1";
|
||||
else if(source === 'Dailymotion') videoUrl = "https://www.dailymotion.com/embed/video/" + videoId + "?autoplay=true";
|
||||
return this._sanitizer.bypassSecurityTrustResourceUrl(videoUrl);
|
||||
}
|
||||
|
||||
|
||||
// retourne la classe CSS de videoCell
|
||||
getClassOfVideoCell(video0): string
|
||||
{
|
||||
if(video0 === this.video) return "videoCell videoCellFocus" ;
|
||||
else return "videoCell" ;
|
||||
}
|
||||
|
||||
|
||||
onEnterOnSearchBar(event)
|
||||
{
|
||||
if(event.key === 'Enter') this.onSearch();
|
||||
}
|
||||
|
||||
|
||||
playlistExists(): boolean
|
||||
{
|
||||
return ((this.playlist !== null) && (this.playlist !== undefined));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in a new issue