diff options
Diffstat (limited to 'client/src/app')
10 files changed, 174 insertions, 5 deletions
diff --git a/client/src/app/shared/forms/form-validators/index.ts b/client/src/app/shared/forms/form-validators/index.ts index 4c6cc6637..119b5d9bf 100644 --- a/client/src/app/shared/forms/form-validators/index.ts +++ b/client/src/app/shared/forms/form-validators/index.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | export * from './host.validator'; | 1 | export * from './host.validator'; |
2 | export * from './user'; | 2 | export * from './user'; |
3 | export * from './video-report'; | ||
3 | export * from './video'; | 4 | export * from './video'; |
diff --git a/client/src/app/shared/forms/form-validators/video-report.ts b/client/src/app/shared/forms/form-validators/video-report.ts new file mode 100644 index 000000000..036ee1721 --- /dev/null +++ b/client/src/app/shared/forms/form-validators/video-report.ts | |||
@@ -0,0 +1,10 @@ | |||
1 | import { Validators } from '@angular/forms'; | ||
2 | |||
3 | export const VIDEO_REPORT_REASON = { | ||
4 | VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(300) ], | ||
5 | MESSAGES: { | ||
6 | 'required': 'Report reason name is required.', | ||
7 | 'minlength': 'Report reson must be at least 2 characters long.', | ||
8 | 'maxlength': 'Report reson cannot be more than 300 characters long.' | ||
9 | } | ||
10 | }; | ||
diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts index 9d79b2f5e..7094d9a34 100644 --- a/client/src/app/videos/shared/video.service.ts +++ b/client/src/app/videos/shared/video.service.ts | |||
@@ -55,6 +55,17 @@ export class VideoService { | |||
55 | .catch((res) => this.restExtractor.handleError(res)); | 55 | .catch((res) => this.restExtractor.handleError(res)); |
56 | } | 56 | } |
57 | 57 | ||
58 | reportVideo(id: string, reason: string) { | ||
59 | const body = { | ||
60 | reason | ||
61 | }; | ||
62 | const url = VideoService.BASE_VIDEO_URL + id + '/abuse'; | ||
63 | |||
64 | return this.authHttp.post(url, body) | ||
65 | .map(this.restExtractor.extractDataBool) | ||
66 | .catch((res) => this.restExtractor.handleError(res)); | ||
67 | } | ||
68 | |||
58 | private extractVideos(result: ResultList) { | 69 | private extractVideos(result: ResultList) { |
59 | const videosJson = result.data; | 70 | const videosJson = result.data; |
60 | const totalVideos = result.total; | 71 | const totalVideos = result.total; |
diff --git a/client/src/app/videos/video-watch/index.ts b/client/src/app/videos/video-watch/index.ts index 1a8403b0a..ed0ed2fc0 100644 --- a/client/src/app/videos/video-watch/index.ts +++ b/client/src/app/videos/video-watch/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './video-magnet.component'; | 1 | export * from './video-magnet.component'; |
2 | export * from './video-share.component'; | 2 | export * from './video-share.component'; |
3 | export * from './video-report.component'; | ||
3 | export * from './video-watch.component'; | 4 | export * from './video-watch.component'; |
4 | export * from './webtorrent.service'; | 5 | export * from './webtorrent.service'; |
diff --git a/client/src/app/videos/video-watch/video-report.component.html b/client/src/app/videos/video-watch/video-report.component.html new file mode 100644 index 000000000..741080ead --- /dev/null +++ b/client/src/app/videos/video-watch/video-report.component.html | |||
@@ -0,0 +1,38 @@ | |||
1 | <div bsModal #modal="bs-modal" class="modal" tabindex="-1"> | ||
2 | <div class="modal-dialog"> | ||
3 | <div class="modal-content modal-lg"> | ||
4 | |||
5 | <div class="modal-header"> | ||
6 | <button type="button" class="close" aria-label="Close" (click)="hide()"> | ||
7 | <span aria-hidden="true">×</span> | ||
8 | </button> | ||
9 | <h4 class="modal-title">Report video</h4> | ||
10 | </div> | ||
11 | |||
12 | <div class="modal-body"> | ||
13 | |||
14 | <form novalidate [formGroup]="form"> | ||
15 | <div class="form-group"> | ||
16 | <label for="description">Reason</label> | ||
17 | <textarea | ||
18 | id="reason" class="form-control" placeholder="Reason..." | ||
19 | formControlName="reason" | ||
20 | > | ||
21 | </textarea> | ||
22 | <div *ngIf="formErrors.reason" class="alert alert-danger"> | ||
23 | {{ formErrors.reason }} | ||
24 | </div> | ||
25 | </div> | ||
26 | |||
27 | <div class="form-group"> | ||
28 | <input | ||
29 | type="button" value="Report" class="btn btn-default form-control" | ||
30 | [disabled]="!form.valid" (click)="report()" | ||
31 | > | ||
32 | </div> | ||
33 | </form> | ||
34 | |||
35 | </div> | ||
36 | </div> | ||
37 | </div> | ||
38 | </div> | ||
diff --git a/client/src/app/videos/video-watch/video-report.component.ts b/client/src/app/videos/video-watch/video-report.component.ts new file mode 100644 index 000000000..7bc1677ab --- /dev/null +++ b/client/src/app/videos/video-watch/video-report.component.ts | |||
@@ -0,0 +1,68 @@ | |||
1 | import { Component, Input, OnInit, ViewChild } from '@angular/core'; | ||
2 | import { FormBuilder, FormGroup } from '@angular/forms'; | ||
3 | |||
4 | import { ModalDirective } from 'ng2-bootstrap/modal'; | ||
5 | |||
6 | import { FormReactive, VIDEO_REPORT_REASON } from '../../shared'; | ||
7 | import { Video, VideoService } from '../shared'; | ||
8 | |||
9 | @Component({ | ||
10 | selector: 'my-video-report', | ||
11 | templateUrl: './video-report.component.html' | ||
12 | }) | ||
13 | export class VideoReportComponent extends FormReactive implements OnInit { | ||
14 | @Input() video: Video = null; | ||
15 | |||
16 | @ViewChild('modal') modal: ModalDirective; | ||
17 | |||
18 | error: string = null; | ||
19 | form: FormGroup; | ||
20 | formErrors = { | ||
21 | reason: '' | ||
22 | }; | ||
23 | validationMessages = { | ||
24 | reason: VIDEO_REPORT_REASON.MESSAGES | ||
25 | }; | ||
26 | |||
27 | constructor( | ||
28 | private formBuilder: FormBuilder, | ||
29 | private videoService: VideoService | ||
30 | ) { | ||
31 | super(); | ||
32 | } | ||
33 | |||
34 | ngOnInit() { | ||
35 | this.buildForm(); | ||
36 | } | ||
37 | |||
38 | buildForm() { | ||
39 | this.form = this.formBuilder.group({ | ||
40 | reason: [ '', VIDEO_REPORT_REASON.VALIDATORS ] | ||
41 | }); | ||
42 | |||
43 | this.form.valueChanges.subscribe(data => this.onValueChanged(data)); | ||
44 | } | ||
45 | |||
46 | show() { | ||
47 | this.modal.show(); | ||
48 | } | ||
49 | |||
50 | hide() { | ||
51 | this.modal.hide(); | ||
52 | } | ||
53 | |||
54 | report() { | ||
55 | const reason = this.form.value['reason'] | ||
56 | |||
57 | this.videoService.reportVideo(this.video.id, reason) | ||
58 | .subscribe( | ||
59 | // TODO: move alert to beautiful notifications | ||
60 | ok => { | ||
61 | alert('Video reported.'); | ||
62 | this.hide(); | ||
63 | }, | ||
64 | |||
65 | err => alert(err.text) | ||
66 | ) | ||
67 | } | ||
68 | } | ||
diff --git a/client/src/app/videos/video-watch/video-watch.component.html b/client/src/app/videos/video-watch/video-watch.component.html index a726ef3ff..8cee9959d 100644 --- a/client/src/app/videos/video-watch/video-watch.component.html +++ b/client/src/app/videos/video-watch/video-watch.component.html | |||
@@ -60,6 +60,19 @@ | |||
60 | <button title="Get magnet URI" id="magnet-uri" class="btn btn-default" (click)="showMagnetUriModal()"> | 60 | <button title="Get magnet URI" id="magnet-uri" class="btn btn-default" (click)="showMagnetUriModal()"> |
61 | <span class="glyphicon glyphicon-magnet"></span> Magnet | 61 | <span class="glyphicon glyphicon-magnet"></span> Magnet |
62 | </button> | 62 | </button> |
63 | |||
64 | <div *ngIf="isUserLoggedIn()" class="btn-group" dropdown> | ||
65 | <button id="single-button" type="button" id="more" class="btn btn-default" dropdownToggle> | ||
66 | <span class="glyphicon glyphicon-option-horizontal"></span> More | ||
67 | </button> | ||
68 | <ul dropdownMenu id="more-menu" role="menu" aria-labelledby="single-button"> | ||
69 | <li role="menuitem"> | ||
70 | <a class="dropdown-item" href="#" (click)="showReportModal($event)"> | ||
71 | <span class="glyphicon glyphicon-alert"></span> Report | ||
72 | </a> | ||
73 | </li> | ||
74 | </ul> | ||
75 | </div> | ||
63 | </div> | 76 | </div> |
64 | </div> | 77 | </div> |
65 | 78 | ||
@@ -79,5 +92,8 @@ | |||
79 | </div> | 92 | </div> |
80 | </div> | 93 | </div> |
81 | 94 | ||
82 | <my-video-share #videoShareModal *ngIf="video !== null" [video]="video"></my-video-share> | 95 | <template [ngIf]="video !== null"> |
83 | <my-video-magnet #videoMagnetModal *ngIf="video !== null" [video]="video"></my-video-magnet> | 96 | <my-video-share #videoShareModal [video]="video"></my-video-share> |
97 | <my-video-magnet #videoMagnetModal [video]="video"></my-video-magnet> | ||
98 | <my-video-report #videoReportModal [video]="video"></my-video-report> | ||
99 | </template> | ||
diff --git a/client/src/app/videos/video-watch/video-watch.component.scss b/client/src/app/videos/video-watch/video-watch.component.scss index ac62b04e7..794412707 100644 --- a/client/src/app/videos/video-watch/video-watch.component.scss +++ b/client/src/app/videos/video-watch/video-watch.component.scss | |||
@@ -47,10 +47,14 @@ | |||
47 | top: 2px; | 47 | top: 2px; |
48 | } | 48 | } |
49 | 49 | ||
50 | #magnet-uri, #share { | 50 | #magnet-uri, #share, #more { |
51 | font-weight: bold; | 51 | font-weight: bold; |
52 | opacity: 0.85; | 52 | opacity: 0.85; |
53 | } | 53 | } |
54 | |||
55 | #more-menu .dropdown-item .glyphicon { | ||
56 | margin-right: 5px; | ||
57 | } | ||
54 | } | 58 | } |
55 | 59 | ||
56 | #video-by-date { | 60 | #video-by-date { |
diff --git a/client/src/app/videos/video-watch/video-watch.component.ts b/client/src/app/videos/video-watch/video-watch.component.ts index 256ffef99..d83cc5a7a 100644 --- a/client/src/app/videos/video-watch/video-watch.component.ts +++ b/client/src/app/videos/video-watch/video-watch.component.ts | |||
@@ -5,8 +5,10 @@ import { ActivatedRoute } from '@angular/router'; | |||
5 | import { MetaService } from 'ng2-meta'; | 5 | import { MetaService } from 'ng2-meta'; |
6 | import * as videojs from 'video.js'; | 6 | import * as videojs from 'video.js'; |
7 | 7 | ||
8 | import { AuthService } from '../../core'; | ||
8 | import { VideoMagnetComponent } from './video-magnet.component'; | 9 | import { VideoMagnetComponent } from './video-magnet.component'; |
9 | import { VideoShareComponent } from './video-share.component'; | 10 | import { VideoShareComponent } from './video-share.component'; |
11 | import { VideoReportComponent } from './video-report.component'; | ||
10 | import { Video, VideoService } from '../shared'; | 12 | import { Video, VideoService } from '../shared'; |
11 | import { WebTorrentService } from './webtorrent.service'; | 13 | import { WebTorrentService } from './webtorrent.service'; |
12 | 14 | ||
@@ -21,6 +23,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
21 | 23 | ||
22 | @ViewChild('videoMagnetModal') videoMagnetModal: VideoMagnetComponent; | 24 | @ViewChild('videoMagnetModal') videoMagnetModal: VideoMagnetComponent; |
23 | @ViewChild('videoShareModal') videoShareModal: VideoShareComponent; | 25 | @ViewChild('videoShareModal') videoShareModal: VideoShareComponent; |
26 | @ViewChild('videoReportModal') videoReportModal: VideoReportComponent; | ||
24 | 27 | ||
25 | downloadSpeed: number; | 28 | downloadSpeed: number; |
26 | error: boolean = false; | 29 | error: boolean = false; |
@@ -42,7 +45,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
42 | private route: ActivatedRoute, | 45 | private route: ActivatedRoute, |
43 | private videoService: VideoService, | 46 | private videoService: VideoService, |
44 | private metaService: MetaService, | 47 | private metaService: MetaService, |
45 | private webTorrentService: WebTorrentService | 48 | private webTorrentService: WebTorrentService, |
49 | private authService: AuthService | ||
46 | ) {} | 50 | ) {} |
47 | 51 | ||
48 | ngOnInit() { | 52 | ngOnInit() { |
@@ -123,6 +127,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
123 | }); | 127 | }); |
124 | } | 128 | } |
125 | 129 | ||
130 | showReportModal(event: Event) { | ||
131 | event.preventDefault(); | ||
132 | this.videoReportModal.show(); | ||
133 | } | ||
134 | |||
126 | showShareModal() { | 135 | showShareModal() { |
127 | this.videoShareModal.show(); | 136 | this.videoShareModal.show(); |
128 | } | 137 | } |
@@ -131,6 +140,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
131 | this.videoMagnetModal.show(); | 140 | this.videoMagnetModal.show(); |
132 | } | 141 | } |
133 | 142 | ||
143 | isUserLoggedIn() { | ||
144 | return this.authService.isLoggedIn(); | ||
145 | } | ||
146 | |||
134 | private loadTooLong() { | 147 | private loadTooLong() { |
135 | this.error = true; | 148 | this.error = true; |
136 | console.error('The video load seems to be abnormally long.'); | 149 | console.error('The video load seems to be abnormally long.'); |
diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts index fb2f453b0..03dea17b5 100644 --- a/client/src/app/videos/videos.module.ts +++ b/client/src/app/videos/videos.module.ts | |||
@@ -4,7 +4,13 @@ import { VideosRoutingModule } from './videos-routing.module'; | |||
4 | import { VideosComponent } from './videos.component'; | 4 | import { VideosComponent } from './videos.component'; |
5 | import { VideoAddComponent } from './video-add'; | 5 | import { VideoAddComponent } from './video-add'; |
6 | import { VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list'; | 6 | import { VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list'; |
7 | import { VideoWatchComponent, VideoMagnetComponent, VideoShareComponent, WebTorrentService } from './video-watch'; | 7 | import { |
8 | VideoWatchComponent, | ||
9 | VideoMagnetComponent, | ||
10 | VideoReportComponent, | ||
11 | VideoShareComponent, | ||
12 | WebTorrentService | ||
13 | } from './video-watch'; | ||
8 | import { LoaderComponent, VideoService } from './shared'; | 14 | import { LoaderComponent, VideoService } from './shared'; |
9 | import { SharedModule } from '../shared'; | 15 | import { SharedModule } from '../shared'; |
10 | 16 | ||
@@ -26,6 +32,7 @@ import { SharedModule } from '../shared'; | |||
26 | VideoWatchComponent, | 32 | VideoWatchComponent, |
27 | VideoMagnetComponent, | 33 | VideoMagnetComponent, |
28 | VideoShareComponent, | 34 | VideoShareComponent, |
35 | VideoReportComponent, | ||
29 | 36 | ||
30 | LoaderComponent | 37 | LoaderComponent |
31 | ], | 38 | ], |