Skip to content

Commit

Permalink
Merge pull request #15 from fga-eps-mds/18-proximo_video
Browse files Browse the repository at this point in the history
US18 - Eu, como usuário, quero a opção de pular para o próximo vídeo, para ter uma experiência contínua de visualização.
  • Loading branch information
victorleaoo authored Aug 25, 2024
2 parents a9beabe + fcfce73 commit 0054c36
Show file tree
Hide file tree
Showing 8 changed files with 1,491 additions and 305 deletions.
91 changes: 42 additions & 49 deletions src/app/pages/video-viewer/video-viewer.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,75 +4,68 @@
-webkit-line-clamp: 3;
overflow: hidden;
}
.button-container {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 1.25rem;
}

.watch-later-button {
.favorite-button, .watch-later-button, .next-video-button {
background-color: #0087c8;
color: #fff;
border: none;
padding: 0.15em 0.9em;
border-radius: 5em;
padding: 0.5rem 1rem;
border-radius: 2.5rem;
cursor: pointer;
font-size: 1.2em;
font-size: 1.2rem;
display: flex;
align-items: center;
gap: 0.3em;
}

.watch-later-button.watch-later-active {
background-color: #f00; /* Cor para quando o vídeo estiver na lista de assistir mais tarde */
}

.watch-later-button:hover {
background-color: #00000094; /* Fundo ao passar o mouse */
color: white; /* Cor das letras ao passar o mouse */
}

.watch-later-button:active {
background-color: red; /* Fundo ao clicar */
color: white; /* Cor das letras ao clicar */
gap: 0.5rem;
transition: background-color 0.3s, transform 0.2s;
}

.title-button-container {
display: flex;
justify-content: space-between;
align-items: center;
.favorite-button.favorited, .watch-later-button.watch-later-active {
background-color: #f00;
}

.favorite-button {
color: white;
background-color: #0087c8;
padding: 0.15em 0.9em;
border: none;
cursor: pointer;
border-radius: 5em;
font-size: 1.2em;
display: flex; /* Utiliza flexbox para alinhar o conteúdo */
align-items: center; /* Alinha verticalmente o conteúdo */
gap: 0.3em;
.favorite-button:hover, .watch-later-button:hover, .next-video-button:hover {
background-color: #00000094;
color: #fff;
transform: scale(1.05);
}

.favorite-button.favorited {
background-color: red; /* Fundo quando favorito */
color: white; /* Cor das letras quando favorito */
.favorite-button:active, .watch-later-button:active, .next-video-button:active {
background-color: red;
color: #fff;
transform: scale(0.95);
}

.favorite-icon {
display: flex;
width: 1em; /* Define a largura do ícone */
height: 1em; /* Define a altura do ícone */
fill: #ffffff; /* Define a cor do ícone */
width: 1.2rem;
height: 1.2rem;
fill: #ffffff;
}

.favorite-button:hover {
background-color: #0085c894; /* Fundo ao passar o mouse */
color: white; /* Cor das letras ao passar o mouse */
.next-video-button {
background-color: #00a651;
}

.favorite-button:active {
background-color: red; /* Fundo ao clicar */
color: white; /* Cor das letras ao clicar */
.next-video-button:hover {
background-color: #008c4a;
}

button.favorited span {
color: rgb(255, 255, 255);
@media (max-width: 1180px) {
.button-container {
flex-direction: column;
gap: 0.7rem;
align-items: flex-start;
font-size: 0.5rem;
line-height: 1rem;
}

.favorite-button, .watch-later-button, .next-video-button {
width: 100%;
justify-content: center;
}
}
6 changes: 5 additions & 1 deletion src/app/pages/video-viewer/video-viewer.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<h4 class="font-custom font-bold text-gray-800">
{{ video.title }}
</h4>
<div class="flex gap-2" *ngIf="userId!==''">
<div class="button-container" *ngIf="userId!==''">
<button class="favorite-button" (click)="toggleFavorite()" [class.favorited]="isFavorite">
<img alt="coracao" src="../../../assets/coracao.svg" class="favorite-icon"/>
<span *ngIf="isFavorite"> Favoritado</span>
Expand All @@ -24,6 +24,10 @@ <h4 class="font-custom font-bold text-gray-800">
<button class="watch-later-button" (click)="toggleWatchLater()" [ngClass]="{'watch-later-active': isWatchLater}">
{{ isWatchLater ? 'Remover da lista': 'Assistir Mais Tarde'}}
</button>
<button class="next-video-button" [title]="titleNextVideo" (click)="nextVideo()">
Próximo Vídeo
<img src="../../../assets/arrow.png" alt="Seta" class="arrow">
</button>
</div>
</div>
<div
Expand Down
79 changes: 79 additions & 0 deletions src/app/pages/video-viewer/video-viewer.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { AlertService } from 'src/app/services/alert.service';
import { ActivatedRoute } from '@angular/router';
import { of, throwError } from 'rxjs';
import { HttpResponse } from '@angular/common/http';
import { IVideo } from 'src/shared/model/video.model';
import * as jwt_decode from 'jwt-decode';

// Mocks
Expand All @@ -23,6 +24,29 @@ class VideoServiceMock {
return of(new HttpResponse({ body: mockVideo }));
}

//Procurar próximo vídeo
findAll() {
const mockResponse = {
body: {
videoList: [
{ id: 1, title: 'Video 1', description: 'Description 1' },
{ id: 2, title: 'Video 2', description: 'Description 2' },
]
}
};
return of(new HttpResponse({ body: mockResponse.body }));
}

checkRecord(userId: string) {
const mockRecord = {
videos: {
1: '2024-01-01T00:00:00Z',
2: '2024-01-02T00:00:00Z',
},
};
return of(mockRecord);
}

// Assistir mais tarde
addToWatchLater(videoId: string, userId: string) {
return of({ message: 'Added to watch later list' });
Expand Down Expand Up @@ -320,6 +344,61 @@ describe('VideoViewerComponent', () => {
expect(addToRecordSpy(component.userId, component.idVideo.toString()).subscribe).toBeDefined();
});

it('should filter videos by record and set filteredVideos correctly video-viewer', () => {
const recordVideos = {
videos: {
190329: true,
190330: true,
},
};

const unbTvVideos = [
{ id: 190329, title: 'Video Title 1' },
{ id: 190330, title: 'Video Title 2' },
{ id: 190331, title: 'Video Title 3' },
];

component.recordVideos = recordVideos;
component.unbTvVideos = unbTvVideos;

component.filterVideosByRecord();
fixture.detectChanges();

const expectedFilteredVideos = [
{ id: 190329, title: 'Video Title 1' },
{ id: 190330, title: 'Video Title 2' },
];

expect(component.filteredVideos).toEqual(expectedFilteredVideos);
});

it('should filter videos by channel and populate unbTvVideos video-viewer', () => {
const mockVideos: IVideo[] = [
{ id: 1, title: 'Video 1', channels: [{ id: 12, name: "unbtvchannel" }] },
{ id: 2, title: 'Video 2', channels: [{ id: 13, name: "otherchannel" }] }
];

component.unbTvChannelId = 12;
component.unbTvVideos = [];

component.filterVideosByChannel(mockVideos);

expect(component.unbTvVideos.length).toBe(1);
expect(component.unbTvVideos[0].id).toBe(1);
});

it('should call checkRecord service method and set recordVideos video-viewer', async () => {
const expectedResponse = [{ id: 1, title: 'Video 1' }];
const checkRecordSpy = spyOn(videoService, 'checkRecord').and.returnValue(of(expectedResponse));

component.userId = '12345';

await component.checkRecord();

expect(checkRecordSpy).toHaveBeenCalledWith('12345');
expect(component.recordVideos).toEqual(expectedResponse);
});

it('should check tracking status and set trackingEnabled correctly', fakeAsync(() => {
const mySpy = spyOn(videoService, 'checkTrackingStatus').and.returnValue(of({ track_enabled: true }));
component.userId = '1';
Expand Down
108 changes: 106 additions & 2 deletions src/app/pages/video-viewer/video-viewer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { UserService } from 'src/app/services/user.service';
import { AlertService } from 'src/app/services/alert.service';
import { AuthService } from 'src/app/services/auth.service';
import jwt_decode from 'jwt-decode';
import { UNB_TV_CHANNEL_ID } from 'src/app/app.constant';
import { Catalog } from 'src/shared/model/catalog.model';
import { Router } from '@angular/router';

@Component({
selector: 'app-video-viewer',
Expand All @@ -25,12 +28,26 @@ export class VideoViewerComponent implements OnInit {
userId: string = '';
user: any;
trackingEnabled: boolean = true; // Estado do rastreamento
unbTvChannelId = UNB_TV_CHANNEL_ID;
videosEduplay: IVideo[] = [];
unbTvVideos: IVideo[] = [];
catalog: Catalog = new Catalog();
categoryVideo: any;
videosAssistidos: IVideo[] = [];
recordVideos: any;
filteredVideos: IVideo[] = [];
program: any;
videosByCategory: IVideo[] = [];
idNextVideo: number;
titleNextVideo: any;
showTitleNextVideo: boolean = false;

expandDescription() {
this.showDescription = !this.showDescription;
}

constructor(
private router: Router,
private route: ActivatedRoute,
private authService: AuthService,
private videoService: VideoService,
Expand All @@ -54,7 +71,94 @@ ngOnInit(): void {

this.findVideoById();
iframe.src = this.eduplayVideoUrl + this.idVideo;
}
this.checkRecord();
this.findAll();
}

checkRecord(): Promise<void> {
return new Promise((resolve, reject) => {
this.videoService.checkRecord(this.userId.toString()).subscribe({
next: (response) => {
this.recordVideos = response;
resolve();
},
error: (err) => {
console.error('Error checking record', err);
reject(err);
}
});
});
}

//Função responsável por trazer todos os vídeos já assistidos pelo usuário
filterVideosByRecord(): void {
const keys = Object.keys(this.recordVideos.videos).map(id => parseInt(id, 10))
this.filteredVideos = this.unbTvVideos.filter(video => video.id !== undefined && keys.includes(video.id));
}

filterVideosByChannel(videos: IVideo[]): void {
videos.forEach((video) => {
const channel = video?.channels;
if (channel && channel[0].id === this.unbTvChannelId) {
this.unbTvVideos.push(video);
}
});
}

findAll(): void {
this.videoService.findAll().subscribe({
next: (data) => {
this.videosEduplay = data.body?.videoList ?? [];
this.filterVideosByChannel(this.videosEduplay);
this.videoService.videosCatalog(this.unbTvVideos, this.catalog)

//Loop para encontrar a categoria do vídeo atual
this.unbTvVideos.forEach((video) => {
if(video.id == this.idVideo){
this.categoryVideo = video.catalog
return;
}
})
//Chamada de função para encontrar o programa do vídeo atual
this.program = this.videoService.findProgramName(this.catalog, this.categoryVideo, this.idVideo);

this.videosByCategory = this.videoService.filterVideosByCategory(this.unbTvVideos, this.categoryVideo);
//console.log("vídeos da categoria do atual: ", this.videosByCategory)
this.filterVideosByRecord();
//console.log("videos assistidos: ", this.filteredVideos)
this.idNextVideo = this.videoService.recommendVideo(this.videosByCategory, this.catalog, this.categoryVideo, this.filteredVideos, this.program);
//Se o id for diferente de -1, o usuário ainda não viu todos os vídeos da categoria atual
if(this.idNextVideo != -1){
//Loop para encontrar o título do próximo vídeo
this.unbTvVideos.forEach((video) => {
if(video.id == this.idNextVideo){
this.titleNextVideo = video.title;
return;
}
})
}else{
this.titleNextVideo = "Não há vídeo para ser recomendado"
}
//console.log("id do próximo vídeo: ", this.idNextVideo)
//console.log("título do próximo vídeo: ", this.titleNextVideo)
},
error: (error) => {
console.log(error);
}
});
}

nextVideo(): void {
if(this.idNextVideo != -1){
this.router.navigate([`/video/${this.idNextVideo}`]).then(() => {
window.location.reload();
});
}else{
this.router.navigate([`/catalog`]).then(() => {
window.location.reload();
});
}
}

setUserIdFromToken(token: string) {
const decodedToken: any = jwt_decode(token);
Expand Down Expand Up @@ -206,4 +310,4 @@ async checkTrackingStatus(): Promise<void> {
console.warn('A API de compartilhamento não é suportada neste navegador.');
}
}
}
}
Loading

0 comments on commit 0054c36

Please sign in to comment.