diff options
17 files changed, 208 insertions, 173 deletions
diff --git a/client/.angular-cli.json b/client/.angular-cli.json index 643e1b319..cb387db0a 100644 --- a/client/.angular-cli.json +++ b/client/.angular-cli.json | |||
@@ -8,12 +8,7 @@ | |||
8 | "root": "src", | 8 | "root": "src", |
9 | "outDir": "dist", | 9 | "outDir": "dist", |
10 | "assets": [ | 10 | "assets": [ |
11 | { | 11 | "./assets/images", |
12 | "glob": "**/*", | ||
13 | "input": "./assets/images", | ||
14 | "output": "./client/assets/images", | ||
15 | "allowOutsideOutDir": false | ||
16 | }, | ||
17 | "./manifest.json" | 12 | "./manifest.json" |
18 | ], | 13 | ], |
19 | "deployUrl": "client/", | 14 | "deployUrl": "client/", |
diff --git a/client/src/app/+admin/config/config.routes.ts b/client/src/app/+admin/config/config.routes.ts index a46b0ddfd..2ca2f8fde 100644 --- a/client/src/app/+admin/config/config.routes.ts +++ b/client/src/app/+admin/config/config.routes.ts | |||
@@ -23,7 +23,7 @@ export const ConfigRoutes: Routes = [ | |||
23 | component: EditCustomConfigComponent, | 23 | component: EditCustomConfigComponent, |
24 | data: { | 24 | data: { |
25 | meta: { | 25 | meta: { |
26 | title: 'Following list' | 26 | title: 'Edit custom configuration' |
27 | } | 27 | } |
28 | } | 28 | } |
29 | } | 29 | } |
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index 027268536..ccec89a8e 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts | |||
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core' | |||
2 | import { FormBuilder, FormGroup } from '@angular/forms' | 2 | import { FormBuilder, FormGroup } from '@angular/forms' |
3 | import { Router } from '@angular/router' | 3 | import { Router } from '@angular/router' |
4 | import { ConfigService } from '@app/+admin/config/shared/config.service' | 4 | import { ConfigService } from '@app/+admin/config/shared/config.service' |
5 | import { ConfirmService } from '@app/core' | ||
5 | import { ServerService } from '@app/core/server/server.service' | 6 | import { ServerService } from '@app/core/server/server.service' |
6 | import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared' | 7 | import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared' |
7 | import { | 8 | import { |
@@ -61,12 +62,16 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
61 | userVideoQuota: USER_VIDEO_QUOTA.MESSAGES | 62 | userVideoQuota: USER_VIDEO_QUOTA.MESSAGES |
62 | } | 63 | } |
63 | 64 | ||
65 | private oldCustomJavascript: string | ||
66 | private oldCustomCSS: string | ||
67 | |||
64 | constructor ( | 68 | constructor ( |
65 | private formBuilder: FormBuilder, | 69 | private formBuilder: FormBuilder, |
66 | private router: Router, | 70 | private router: Router, |
67 | private notificationsService: NotificationsService, | 71 | private notificationsService: NotificationsService, |
68 | private configService: ConfigService, | 72 | private configService: ConfigService, |
69 | private serverService: ServerService | 73 | private serverService: ServerService, |
74 | private confirmService: ConfirmService | ||
70 | ) { | 75 | ) { |
71 | super() | 76 | super() |
72 | } | 77 | } |
@@ -109,6 +114,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
109 | res => { | 114 | res => { |
110 | this.customConfig = res | 115 | this.customConfig = res |
111 | 116 | ||
117 | this.oldCustomCSS = this.customConfig.instance.customizations.css | ||
118 | this.oldCustomJavascript = this.customConfig.instance.customizations.javascript | ||
119 | |||
112 | this.updateForm() | 120 | this.updateForm() |
113 | }, | 121 | }, |
114 | 122 | ||
@@ -124,7 +132,27 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
124 | return this.form.value['signupEnabled'] === true | 132 | return this.form.value['signupEnabled'] === true |
125 | } | 133 | } |
126 | 134 | ||
127 | formValidated () { | 135 | async formValidated () { |
136 | const newCustomizationJavascript = this.form.value['customizationJavascript'] | ||
137 | const newCustomizationCSS = this.form.value['customizationCSS'] | ||
138 | |||
139 | const customizations = [] | ||
140 | if (newCustomizationJavascript && newCustomizationJavascript !== this.oldCustomJavascript) customizations.push('JavaScript') | ||
141 | if (newCustomizationCSS && newCustomizationCSS !== this.oldCustomCSS) customizations.push('CSS') | ||
142 | |||
143 | if (customizations.length !== 0) { | ||
144 | const customizationsText = customizations.join('/') | ||
145 | |||
146 | const message = `You set custom ${customizationsText}. ` + | ||
147 | 'This could lead to security issues or bugs if you do not understand it. ' + | ||
148 | 'Are you sure you want to update the configuration?' | ||
149 | const label = `Please type "I understand the ${customizationsText} I set" to confirm.` | ||
150 | const expectedInputValue = `I understand the ${customizationsText} I set` | ||
151 | |||
152 | const confirmRes = await this.confirmService.confirmWithInput(message, label, expectedInputValue) | ||
153 | if (confirmRes === false) return | ||
154 | } | ||
155 | |||
128 | const data = { | 156 | const data = { |
129 | instance: { | 157 | instance: { |
130 | name: this.form.value['instanceName'], | 158 | name: this.form.value['instanceName'], |
diff --git a/client/src/app/+admin/follows/following-add/following-add.component.ts b/client/src/app/+admin/follows/following-add/following-add.component.ts index bf842129d..c296c8852 100644 --- a/client/src/app/+admin/follows/following-add/following-add.component.ts +++ b/client/src/app/+admin/follows/following-add/following-add.component.ts | |||
@@ -43,7 +43,7 @@ export class FollowingAddComponent { | |||
43 | } | 43 | } |
44 | } | 44 | } |
45 | 45 | ||
46 | addFollowing () { | 46 | async addFollowing () { |
47 | this.error = '' | 47 | this.error = '' |
48 | 48 | ||
49 | const hosts = this.getNotEmptyHosts() | 49 | const hosts = this.getNotEmptyHosts() |
@@ -57,20 +57,17 @@ export class FollowingAddComponent { | |||
57 | } | 57 | } |
58 | 58 | ||
59 | const confirmMessage = 'If you confirm, you will send a follow request to:<br /> - ' + hosts.join('<br /> - ') | 59 | const confirmMessage = 'If you confirm, you will send a follow request to:<br /> - ' + hosts.join('<br /> - ') |
60 | this.confirmService.confirm(confirmMessage, 'Follow new server(s)').subscribe( | 60 | const res = await this.confirmService.confirm(confirmMessage, 'Follow new server(s)') |
61 | res => { | 61 | if (res === false) return |
62 | if (res === false) return | ||
63 | 62 | ||
64 | this.followService.follow(hosts).subscribe( | 63 | this.followService.follow(hosts).subscribe( |
65 | status => { | 64 | () => { |
66 | this.notificationsService.success('Success', 'Follow request(s) sent!') | 65 | this.notificationsService.success('Success', 'Follow request(s) sent!') |
67 | 66 | ||
68 | setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500) | 67 | setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500) |
69 | }, | 68 | }, |
70 | 69 | ||
71 | err => this.notificationsService.error('Error', err.message) | 70 | err => this.notificationsService.error('Error', err.message) |
72 | ) | ||
73 | } | ||
74 | ) | 71 | ) |
75 | } | 72 | } |
76 | 73 | ||
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts index d4f8d0309..ad1bd4536 100644 --- a/client/src/app/+admin/follows/following-list/following-list.component.ts +++ b/client/src/app/+admin/follows/following-list/following-list.component.ts | |||
@@ -25,20 +25,17 @@ export class FollowingListComponent extends RestTable { | |||
25 | super() | 25 | super() |
26 | } | 26 | } |
27 | 27 | ||
28 | removeFollowing (follow: AccountFollow) { | 28 | async removeFollowing (follow: AccountFollow) { |
29 | this.confirmService.confirm(`Do you really want to unfollow ${follow.following.host}?`, 'Unfollow').subscribe( | 29 | const res = await this.confirmService.confirm(`Do you really want to unfollow ${follow.following.host}?`, 'Unfollow') |
30 | res => { | 30 | if (res === false) return |
31 | if (res === false) return | ||
32 | 31 | ||
33 | this.followService.unfollow(follow).subscribe( | 32 | this.followService.unfollow(follow).subscribe( |
34 | () => { | 33 | () => { |
35 | this.notificationsService.success('Success', `You are not following ${follow.following.host} anymore.`) | 34 | this.notificationsService.success('Success', `You are not following ${follow.following.host} anymore.`) |
36 | this.loadData() | 35 | this.loadData() |
37 | }, | 36 | }, |
38 | 37 | ||
39 | err => this.notificationsService.error('Error', err.message) | 38 | err => this.notificationsService.error('Error', err.message) |
40 | ) | ||
41 | } | ||
42 | ) | 39 | ) |
43 | } | 40 | } |
44 | 41 | ||
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts index 1e8e1af49..512152808 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.ts +++ b/client/src/app/+admin/users/user-list/user-list.component.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { Component } from '@angular/core' | 1 | import { Component } from '@angular/core' |
2 | import { SortMeta } from 'primeng/components/common/sortmeta' | ||
3 | 2 | ||
4 | import { NotificationsService } from 'angular2-notifications' | 3 | import { NotificationsService } from 'angular2-notifications' |
4 | import { SortMeta } from 'primeng/components/common/sortmeta' | ||
5 | 5 | ||
6 | import { ConfirmService } from '../../../core' | 6 | import { ConfirmService } from '../../../core' |
7 | import { RestTable, RestPagination, User } from '../../../shared' | 7 | import { RestPagination, RestTable, User } from '../../../shared' |
8 | import { UserService } from '../shared' | 8 | import { UserService } from '../shared' |
9 | 9 | ||
10 | @Component({ | 10 | @Component({ |
@@ -27,25 +27,22 @@ export class UserListComponent extends RestTable { | |||
27 | super() | 27 | super() |
28 | } | 28 | } |
29 | 29 | ||
30 | removeUser (user: User) { | 30 | async removeUser (user: User) { |
31 | if (user.username === 'root') { | 31 | if (user.username === 'root') { |
32 | this.notificationsService.error('Error', 'You cannot delete root.') | 32 | this.notificationsService.error('Error', 'You cannot delete root.') |
33 | return | 33 | return |
34 | } | 34 | } |
35 | 35 | ||
36 | this.confirmService.confirm('Do you really want to delete this user?', 'Delete').subscribe( | 36 | const res = await this.confirmService.confirm('Do you really want to delete this user?', 'Delete') |
37 | res => { | 37 | if (res === false) return |
38 | if (res === false) return | ||
39 | 38 | ||
40 | this.userService.removeUser(user).subscribe( | 39 | this.userService.removeUser(user).subscribe( |
41 | () => { | 40 | () => { |
42 | this.notificationsService.success('Success', `User ${user.username} deleted.`) | 41 | this.notificationsService.success('Success', `User ${user.username} deleted.`) |
43 | this.loadData() | 42 | this.loadData() |
44 | }, | 43 | }, |
45 | 44 | ||
46 | err => this.notificationsService.error('Error', err.message) | 45 | err => this.notificationsService.error('Error', err.message) |
47 | ) | ||
48 | } | ||
49 | ) | 46 | ) |
50 | } | 47 | } |
51 | 48 | ||
diff --git a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts index 56024b247..f4cf21259 100644 --- a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts +++ b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts | |||
@@ -31,22 +31,19 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { | |||
31 | this.loadData() | 31 | this.loadData() |
32 | } | 32 | } |
33 | 33 | ||
34 | removeVideoFromBlacklist (entry: BlacklistedVideo) { | 34 | async removeVideoFromBlacklist (entry: BlacklistedVideo) { |
35 | const confirmMessage = 'Do you really want to remove this video from the blacklist ? It will be available again in the video list.' | 35 | const confirmMessage = 'Do you really want to remove this video from the blacklist ? It will be available again in the video list.' |
36 | 36 | ||
37 | this.confirmService.confirm(confirmMessage, 'Remove').subscribe( | 37 | const res = await this.confirmService.confirm(confirmMessage, 'Remove') |
38 | res => { | 38 | if (res === false) return |
39 | if (res === false) return | ||
40 | 39 | ||
41 | this.videoBlacklistService.removeVideoFromBlacklist(entry.videoId).subscribe( | 40 | this.videoBlacklistService.removeVideoFromBlacklist(entry.videoId).subscribe( |
42 | status => { | 41 | () => { |
43 | this.notificationsService.success('Success', `Video ${entry.name} removed from the blacklist.`) | 42 | this.notificationsService.success('Success', `Video ${entry.name} removed from the blacklist.`) |
44 | this.loadData() | 43 | this.loadData() |
45 | }, | 44 | }, |
46 | 45 | ||
47 | err => this.notificationsService.error('Error', err.message) | 46 | err => this.notificationsService.error('Error', err.message) |
48 | ) | ||
49 | } | ||
50 | ) | 47 | ) |
51 | } | 48 | } |
52 | 49 | ||
diff --git a/client/src/app/account/account-videos/account-videos.component.ts b/client/src/app/account/account-videos/account-videos.component.ts index e9d044dbf..a286bad1c 100644 --- a/client/src/app/account/account-videos/account-videos.component.ts +++ b/client/src/app/account/account-videos/account-videos.component.ts | |||
@@ -56,55 +56,49 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit | |||
56 | return this.videoService.getMyVideos(newPagination, this.sort) | 56 | return this.videoService.getMyVideos(newPagination, this.sort) |
57 | } | 57 | } |
58 | 58 | ||
59 | deleteSelectedVideos () { | 59 | async deleteSelectedVideos () { |
60 | const toDeleteVideosIds = Object.keys(this.checkedVideos) | 60 | const toDeleteVideosIds = Object.keys(this.checkedVideos) |
61 | .filter(k => this.checkedVideos[k] === true) | 61 | .filter(k => this.checkedVideos[k] === true) |
62 | .map(k => parseInt(k, 10)) | 62 | .map(k => parseInt(k, 10)) |
63 | 63 | ||
64 | this.confirmService.confirm(`Do you really want to delete ${toDeleteVideosIds.length} videos?`, 'Delete').subscribe( | 64 | const res = await this.confirmService.confirm(`Do you really want to delete ${toDeleteVideosIds.length} videos?`, 'Delete') |
65 | res => { | 65 | if (res === false) return |
66 | if (res === false) return | 66 | |
67 | 67 | const observables: Observable<any>[] = [] | |
68 | const observables: Observable<any>[] = [] | 68 | for (const videoId of toDeleteVideosIds) { |
69 | for (const videoId of toDeleteVideosIds) { | 69 | const o = this.videoService |
70 | const o = this.videoService | 70 | .removeVideo(videoId) |
71 | .removeVideo(videoId) | 71 | .do(() => this.spliceVideosById(videoId)) |
72 | .do(() => this.spliceVideosById(videoId)) | 72 | |
73 | 73 | observables.push(o) | |
74 | observables.push(o) | 74 | } |
75 | } | 75 | |
76 | 76 | Observable.from(observables) | |
77 | Observable.from(observables) | 77 | .concatAll() |
78 | .concatAll() | 78 | .subscribe( |
79 | .subscribe( | 79 | res => { |
80 | res => { | 80 | this.notificationsService.success('Success', `${toDeleteVideosIds.length} videos deleted.`) |
81 | this.notificationsService.success('Success', `${toDeleteVideosIds.length} videos deleted.`) | 81 | this.buildVideoPages() |
82 | this.buildVideoPages() | 82 | }, |
83 | }, | 83 | |
84 | 84 | err => this.notificationsService.error('Error', err.message) | |
85 | err => this.notificationsService.error('Error', err.message) | 85 | ) |
86 | ) | ||
87 | } | ||
88 | ) | ||
89 | } | 86 | } |
90 | 87 | ||
91 | deleteVideo (video: Video) { | 88 | async deleteVideo (video: Video) { |
92 | this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete').subscribe( | 89 | const res = await this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete') |
93 | res => { | 90 | if (res === false) return |
94 | if (res === false) return | 91 | |
95 | 92 | this.videoService.removeVideo(video.id) | |
96 | this.videoService.removeVideo(video.id) | 93 | .subscribe( |
97 | .subscribe( | 94 | status => { |
98 | status => { | 95 | this.notificationsService.success('Success', `Video ${video.name} deleted.`) |
99 | this.notificationsService.success('Success', `Video ${video.name} deleted.`) | 96 | this.spliceVideosById(video.id) |
100 | this.spliceVideosById(video.id) | 97 | this.buildVideoPages() |
101 | this.buildVideoPages() | 98 | }, |
102 | }, | 99 | |
103 | 100 | error => this.notificationsService.error('Error', error.message) | |
104 | error => this.notificationsService.error('Error', error.message) | 101 | ) |
105 | ) | ||
106 | } | ||
107 | ) | ||
108 | } | 102 | } |
109 | 103 | ||
110 | private spliceVideosById (id: number) { | 104 | private spliceVideosById (id: number) { |
diff --git a/client/src/app/core/confirm/confirm.component.html b/client/src/app/core/confirm/confirm.component.html index cc2c28de2..90274b248 100644 --- a/client/src/app/core/confirm/confirm.component.html +++ b/client/src/app/core/confirm/confirm.component.html | |||
@@ -10,13 +10,18 @@ | |||
10 | <div class="modal-body" > | 10 | <div class="modal-body" > |
11 | <div [innerHtml]="message"></div> | 11 | <div [innerHtml]="message"></div> |
12 | 12 | ||
13 | <div *ngIf="inputLabel && expectedInputValue" class="form-group"> | ||
14 | <label for="confirmInput">{{ inputLabel }}</label> | ||
15 | <input type="text" id="confirmInput" name="confirmInput" [(ngModel)]="inputValue" /> | ||
16 | </div> | ||
17 | |||
13 | <div class="form-group inputs"> | 18 | <div class="form-group inputs"> |
14 | <span class="action-button action-button-cancel" (click)="cancel()"> | 19 | <span class="action-button action-button-cancel" (click)="cancel()"> |
15 | Cancel | 20 | Cancel |
16 | </span> | 21 | </span> |
17 | 22 | ||
18 | <input | 23 | <input |
19 | type="submit" value="Confirm" class="action-button-submit" | 24 | type="submit" value="Confirm" class="action-button-submit" [disabled]="isConfirmationDisabled()" |
20 | (click)="confirm()" | 25 | (click)="confirm()" |
21 | > | 26 | > |
22 | </div> | 27 | </div> |
diff --git a/client/src/app/core/confirm/confirm.component.scss b/client/src/app/core/confirm/confirm.component.scss new file mode 100644 index 000000000..93dd7926b --- /dev/null +++ b/client/src/app/core/confirm/confirm.component.scss | |||
@@ -0,0 +1,17 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .button { | ||
5 | padding: 0 13px; | ||
6 | } | ||
7 | |||
8 | input[type=text] { | ||
9 | @include peertube-input-text(100%); | ||
10 | display: block; | ||
11 | } | ||
12 | |||
13 | .form-group { | ||
14 | margin: 20px 0; | ||
15 | } | ||
16 | |||
17 | |||
diff --git a/client/src/app/core/confirm/confirm.component.ts b/client/src/app/core/confirm/confirm.component.ts index 0515d969a..8f81b7a98 100644 --- a/client/src/app/core/confirm/confirm.component.ts +++ b/client/src/app/core/confirm/confirm.component.ts | |||
@@ -4,21 +4,20 @@ import { ModalDirective } from 'ngx-bootstrap/modal' | |||
4 | 4 | ||
5 | import { ConfirmService } from './confirm.service' | 5 | import { ConfirmService } from './confirm.service' |
6 | 6 | ||
7 | export interface ConfigChangedEvent { | ||
8 | columns: { [id: string]: { isDisplayed: boolean } } | ||
9 | config: { resultsPerPage: number } | ||
10 | } | ||
11 | |||
12 | @Component({ | 7 | @Component({ |
13 | selector: 'my-confirm', | 8 | selector: 'my-confirm', |
14 | templateUrl: './confirm.component.html', | 9 | templateUrl: './confirm.component.html', |
15 | styles: [ '.button { padding: 0 13px; }' ] | 10 | styleUrls: [ './confirm.component.scss' ] |
16 | }) | 11 | }) |
17 | export class ConfirmComponent implements OnInit { | 12 | export class ConfirmComponent implements OnInit { |
18 | @ViewChild('confirmModal') confirmModal: ModalDirective | 13 | @ViewChild('confirmModal') confirmModal: ModalDirective |
19 | 14 | ||
20 | title = '' | 15 | title = '' |
21 | message = '' | 16 | message = '' |
17 | expectedInputValue = '' | ||
18 | inputLabel = '' | ||
19 | |||
20 | inputValue = '' | ||
22 | 21 | ||
23 | constructor (private confirmService: ConfirmService) { | 22 | constructor (private confirmService: ConfirmService) { |
24 | // Empty | 23 | // Empty |
@@ -31,10 +30,13 @@ export class ConfirmComponent implements OnInit { | |||
31 | } | 30 | } |
32 | 31 | ||
33 | this.confirmService.showConfirm.subscribe( | 32 | this.confirmService.showConfirm.subscribe( |
34 | ({ title, message }) => { | 33 | ({ title, message, expectedInputValue, inputLabel }) => { |
35 | this.title = title | 34 | this.title = title |
36 | this.message = message | 35 | this.message = message |
37 | 36 | ||
37 | this.inputLabel = inputLabel | ||
38 | this.expectedInputValue = expectedInputValue | ||
39 | |||
38 | this.showModal() | 40 | this.showModal() |
39 | } | 41 | } |
40 | ) | 42 | ) |
@@ -52,6 +54,13 @@ export class ConfirmComponent implements OnInit { | |||
52 | this.hideModal() | 54 | this.hideModal() |
53 | } | 55 | } |
54 | 56 | ||
57 | isConfirmationDisabled () { | ||
58 | // No input validation | ||
59 | if (!this.inputLabel || !this.expectedInputValue) return false | ||
60 | |||
61 | return this.expectedInputValue !== this.inputValue | ||
62 | } | ||
63 | |||
55 | showModal () { | 64 | showModal () { |
56 | this.confirmModal.show() | 65 | this.confirmModal.show() |
57 | } | 66 | } |
diff --git a/client/src/app/core/confirm/confirm.service.ts b/client/src/app/core/confirm/confirm.service.ts index f12ff1848..f30feb9d0 100644 --- a/client/src/app/core/confirm/confirm.service.ts +++ b/client/src/app/core/confirm/confirm.service.ts | |||
@@ -1,15 +1,22 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | import { Subject } from 'rxjs/Subject' | 2 | import { Subject } from 'rxjs/Subject' |
3 | import 'rxjs/add/operator/first' | 3 | import 'rxjs/add/operator/first' |
4 | import 'rxjs/add/operator/toPromise' | ||
4 | 5 | ||
5 | @Injectable() | 6 | @Injectable() |
6 | export class ConfirmService { | 7 | export class ConfirmService { |
7 | showConfirm = new Subject<{ title, message }>() | 8 | showConfirm = new Subject<{ title: string, message: string, inputLabel?: string, expectedInputValue?: string }>() |
8 | confirmResponse = new Subject<boolean>() | 9 | confirmResponse = new Subject<boolean>() |
9 | 10 | ||
10 | confirm (message = '', title = '') { | 11 | confirm (message: string, title = '') { |
11 | this.showConfirm.next({ title, message }) | 12 | this.showConfirm.next({ title, message }) |
12 | 13 | ||
13 | return this.confirmResponse.asObservable().first() | 14 | return this.confirmResponse.asObservable().first().toPromise() |
15 | } | ||
16 | |||
17 | confirmWithInput (message: string, inputLabel: string, expectedInputValue: string, title = '') { | ||
18 | this.showConfirm.next({ title, message, inputLabel, expectedInputValue }) | ||
19 | |||
20 | return this.confirmResponse.asObservable().first().toPromise() | ||
14 | } | 21 | } |
15 | } | 22 | } |
diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index eea6f340b..36dbe8b5c 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { CommonModule } from '@angular/common' | 1 | import { CommonModule } from '@angular/common' |
2 | import { NgModule, Optional, SkipSelf } from '@angular/core' | 2 | import { NgModule, Optional, SkipSelf } from '@angular/core' |
3 | import { FormsModule } from '@angular/forms' | ||
3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations' | 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations' |
4 | import { RouterModule } from '@angular/router' | 5 | import { RouterModule } from '@angular/router' |
5 | import { LoadingBarModule } from '@ngx-loading-bar/core' | 6 | import { LoadingBarModule } from '@ngx-loading-bar/core' |
@@ -18,6 +19,7 @@ import { ServerService } from './server' | |||
18 | imports: [ | 19 | imports: [ |
19 | CommonModule, | 20 | CommonModule, |
20 | RouterModule, | 21 | RouterModule, |
22 | FormsModule, | ||
21 | BrowserAnimationsModule, | 23 | BrowserAnimationsModule, |
22 | 24 | ||
23 | ModalModule, | 25 | ModalModule, |
diff --git a/client/src/app/shared/guards/can-deactivate-guard.service.ts b/client/src/app/shared/guards/can-deactivate-guard.service.ts index 15618f699..c3b5f37f8 100644 --- a/client/src/app/shared/guards/can-deactivate-guard.service.ts +++ b/client/src/app/shared/guards/can-deactivate-guard.service.ts | |||
@@ -15,7 +15,7 @@ export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> | |||
15 | currentRoute: ActivatedRouteSnapshot, | 15 | currentRoute: ActivatedRouteSnapshot, |
16 | currentState: RouterStateSnapshot, | 16 | currentState: RouterStateSnapshot, |
17 | nextState: RouterStateSnapshot | 17 | nextState: RouterStateSnapshot |
18 | ): Observable<boolean> | boolean { | 18 | ) { |
19 | const result = component.canDeactivate() | 19 | const result = component.canDeactivate() |
20 | const text = result.text || 'All unsaved data will be lost, are you sure you want to leave this page?' | 20 | const text = result.text || 'All unsaved data will be lost, are you sure you want to leave this page?' |
21 | 21 | ||
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.ts b/client/src/app/videos/+video-watch/comment/video-comments.component.ts index 16f1a0643..711a01ba0 100644 --- a/client/src/app/videos/+video-watch/comment/video-comments.component.ts +++ b/client/src/app/videos/+video-watch/comment/video-comments.component.ts | |||
@@ -109,38 +109,35 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy { | |||
109 | this.viewReplies(commentTree.comment.id) | 109 | this.viewReplies(commentTree.comment.id) |
110 | } | 110 | } |
111 | 111 | ||
112 | onWantedToDelete (commentToDelete: VideoComment) { | 112 | async onWantedToDelete (commentToDelete: VideoComment) { |
113 | let message = 'Do you really want to delete this comment?' | 113 | let message = 'Do you really want to delete this comment?' |
114 | if (commentToDelete.totalReplies !== 0) message += `${commentToDelete.totalReplies} would be deleted too.` | 114 | if (commentToDelete.totalReplies !== 0) message += `${commentToDelete.totalReplies} would be deleted too.` |
115 | 115 | ||
116 | this.confirmService.confirm(message, 'Delete').subscribe( | 116 | const res = await this.confirmService.confirm(message, 'Delete') |
117 | res => { | 117 | if (res === false) return |
118 | if (res === false) return | 118 | |
119 | 119 | this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id) | |
120 | this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id) | 120 | .subscribe( |
121 | .subscribe( | 121 | () => { |
122 | () => { | 122 | // Delete the comment in the tree |
123 | // Delete the comment in the tree | 123 | if (commentToDelete.inReplyToCommentId) { |
124 | if (commentToDelete.inReplyToCommentId) { | 124 | const thread = this.threadComments[commentToDelete.threadId] |
125 | const thread = this.threadComments[commentToDelete.threadId] | 125 | if (!thread) { |
126 | if (!thread) { | 126 | console.error(`Cannot find thread ${commentToDelete.threadId} of the comment to delete ${commentToDelete.id}`) |
127 | console.error(`Cannot find thread ${commentToDelete.threadId} of the comment to delete ${commentToDelete.id}`) | 127 | return |
128 | return | 128 | } |
129 | } | 129 | |
130 | 130 | this.deleteLocalCommentThread(thread, commentToDelete) | |
131 | this.deleteLocalCommentThread(thread, commentToDelete) | 131 | return |
132 | return | 132 | } |
133 | } | 133 | |
134 | 134 | // Delete the thread | |
135 | // Delete the thread | 135 | this.comments = this.comments.filter(c => c.id !== commentToDelete.id) |
136 | this.comments = this.comments.filter(c => c.id !== commentToDelete.id) | 136 | this.componentPagination.totalItems-- |
137 | this.componentPagination.totalItems-- | 137 | }, |
138 | }, | 138 | |
139 | 139 | err => this.notificationsService.error('Error', err.message) | |
140 | err => this.notificationsService.error('Error', err.message) | 140 | ) |
141 | ) | ||
142 | } | ||
143 | ) | ||
144 | } | 141 | } |
145 | 142 | ||
146 | isUserLoggedIn () { | 143 | isUserLoggedIn () { |
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 6b118b1de..d04d50310 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts | |||
@@ -130,24 +130,21 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
130 | } | 130 | } |
131 | } | 131 | } |
132 | 132 | ||
133 | blacklistVideo (event: Event) { | 133 | async blacklistVideo (event: Event) { |
134 | event.preventDefault() | 134 | event.preventDefault() |
135 | 135 | ||
136 | this.confirmService.confirm('Do you really want to blacklist this video?', 'Blacklist').subscribe( | 136 | const res = await this.confirmService.confirm('Do you really want to blacklist this video?', 'Blacklist') |
137 | res => { | 137 | if (res === false) return |
138 | if (res === false) return | ||
139 | 138 | ||
140 | this.videoBlacklistService.blacklistVideo(this.video.id) | 139 | this.videoBlacklistService.blacklistVideo(this.video.id) |
141 | .subscribe( | 140 | .subscribe( |
142 | status => { | 141 | status => { |
143 | this.notificationsService.success('Success', `Video ${this.video.name} had been blacklisted.`) | 142 | this.notificationsService.success('Success', `Video ${this.video.name} had been blacklisted.`) |
144 | this.router.navigate(['/videos/list']) | 143 | this.router.navigate(['/videos/list']) |
145 | }, | 144 | }, |
146 | 145 | ||
147 | error => this.notificationsService.error('Error', error.message) | 146 | error => this.notificationsService.error('Error', error.message) |
148 | ) | 147 | ) |
149 | } | ||
150 | ) | ||
151 | } | 148 | } |
152 | 149 | ||
153 | showMoreDescription () { | 150 | showMoreDescription () { |
@@ -236,26 +233,22 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
236 | return this.video.isRemovableBy(this.authService.getUser()) | 233 | return this.video.isRemovableBy(this.authService.getUser()) |
237 | } | 234 | } |
238 | 235 | ||
239 | removeVideo (event: Event) { | 236 | async removeVideo (event: Event) { |
240 | event.preventDefault() | 237 | event.preventDefault() |
241 | 238 | ||
242 | this.confirmService.confirm('Do you really want to delete this video?', 'Delete') | 239 | const res = await this.confirmService.confirm('Do you really want to delete this video?', 'Delete') |
243 | .subscribe( | 240 | if (res === false) return |
244 | res => { | ||
245 | if (res === false) return | ||
246 | 241 | ||
247 | this.videoService.removeVideo(this.video.id) | 242 | this.videoService.removeVideo(this.video.id) |
248 | .subscribe( | 243 | .subscribe( |
249 | status => { | 244 | status => { |
250 | this.notificationsService.success('Success', `Video ${this.video.name} deleted.`) | 245 | this.notificationsService.success('Success', `Video ${this.video.name} deleted.`) |
251 | 246 | ||
252 | // Go back to the video-list. | 247 | // Go back to the video-list. |
253 | this.router.navigate([ '/videos/list' ]) | 248 | this.router.navigate([ '/videos/list' ]) |
254 | }, | 249 | }, |
255 | 250 | ||
256 | error => this.notificationsService.error('Error', error.message) | 251 | error => this.notificationsService.error('Error', error.message) |
257 | ) | ||
258 | } | ||
259 | ) | 252 | ) |
260 | } | 253 | } |
261 | 254 | ||
diff --git a/server/controllers/client.ts b/server/controllers/client.ts index 2fcca6f76..df2eee9c9 100644 --- a/server/controllers/client.ts +++ b/server/controllers/client.ts | |||
@@ -10,7 +10,7 @@ import { VideoModel } from '../models/video/video' | |||
10 | const clientsRouter = express.Router() | 10 | const clientsRouter = express.Router() |
11 | 11 | ||
12 | const distPath = join(root(), 'client', 'dist') | 12 | const distPath = join(root(), 'client', 'dist') |
13 | const assetsImagesPath = join(root(), 'client', 'dist', 'client', 'assets', 'images') | 13 | const assetsImagesPath = join(root(), 'client', 'dist', 'assets', 'images') |
14 | const embedPath = join(distPath, 'standalone', 'videos', 'embed.html') | 14 | const embedPath = join(distPath, 'standalone', 'videos', 'embed.html') |
15 | const indexPath = join(distPath, 'index.html') | 15 | const indexPath = join(distPath, 'index.html') |
16 | 16 | ||