diff options
Diffstat (limited to 'client/src/app/videos/+video-edit')
9 files changed, 176 insertions, 21 deletions
diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.html b/client/src/app/videos/+video-edit/shared/video-description.component.html new file mode 100644 index 000000000..da66a9753 --- /dev/null +++ b/client/src/app/videos/+video-edit/shared/video-description.component.html | |||
@@ -0,0 +1,9 @@ | |||
1 | <textarea | ||
2 | [(ngModel)]="description" (ngModelChange)="onModelChange()" | ||
3 | id="description" placeholder="My super video"> | ||
4 | </textarea> | ||
5 | |||
6 | <tabset #staticTabs class="previews"> | ||
7 | <tab heading="Truncated description preview" [innerHTML]="truncatedDescriptionHTML"></tab> | ||
8 | <tab heading="Complete description preview" [innerHTML]="descriptionHTML"></tab> | ||
9 | </tabset> | ||
diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.scss b/client/src/app/videos/+video-edit/shared/video-description.component.scss new file mode 100644 index 000000000..38506bb46 --- /dev/null +++ b/client/src/app/videos/+video-edit/shared/video-description.component.scss | |||
@@ -0,0 +1,41 @@ | |||
1 | textarea { | ||
2 | @include peertube-input-text(100%); | ||
3 | |||
4 | padding: 5px 15px; | ||
5 | font-size: 15px; | ||
6 | height: 150px; | ||
7 | } | ||
8 | |||
9 | .previews /deep/ { | ||
10 | font-size: 15px !important; | ||
11 | |||
12 | .nav { | ||
13 | margin-top: 10px; | ||
14 | font-size: 16px !important; | ||
15 | border: none !important; | ||
16 | |||
17 | .nav-item .nav-link { | ||
18 | color: #000 !important; | ||
19 | height: 30px !important; | ||
20 | margin-right: 30px; | ||
21 | padding: 0 15px; | ||
22 | display: flex; | ||
23 | align-items: center; | ||
24 | border-radius: 3px; | ||
25 | border: none !important; | ||
26 | |||
27 | &.active, &:hover { | ||
28 | background-color: #F0F0F0; | ||
29 | } | ||
30 | |||
31 | &.active { | ||
32 | font-weight: $font-semibold !important; | ||
33 | } | ||
34 | } | ||
35 | } | ||
36 | |||
37 | .tab-content { | ||
38 | min-height: 75px; | ||
39 | padding: 15px; | ||
40 | } | ||
41 | } | ||
diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.ts b/client/src/app/videos/+video-edit/shared/video-description.component.ts new file mode 100644 index 000000000..8dfb74b2a --- /dev/null +++ b/client/src/app/videos/+video-edit/shared/video-description.component.ts | |||
@@ -0,0 +1,66 @@ | |||
1 | import { Component, forwardRef, Input, OnInit } from '@angular/core' | ||
2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | ||
3 | import { truncate } from 'lodash' | ||
4 | import 'rxjs/add/operator/debounceTime' | ||
5 | import 'rxjs/add/operator/distinctUntilChanged' | ||
6 | import { Subject } from 'rxjs/Subject' | ||
7 | import { MarkdownService } from '../../shared' | ||
8 | |||
9 | @Component({ | ||
10 | selector: 'my-video-description', | ||
11 | templateUrl: './video-description.component.html', | ||
12 | styleUrls: [ './video-description.component.scss' ], | ||
13 | providers: [ | ||
14 | { | ||
15 | provide: NG_VALUE_ACCESSOR, | ||
16 | useExisting: forwardRef(() => VideoDescriptionComponent), | ||
17 | multi: true | ||
18 | } | ||
19 | ] | ||
20 | }) | ||
21 | |||
22 | export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { | ||
23 | @Input() description = '' | ||
24 | truncatedDescriptionHTML = '' | ||
25 | descriptionHTML = '' | ||
26 | |||
27 | private descriptionChanged = new Subject<string>() | ||
28 | |||
29 | constructor (private markdownService: MarkdownService) {} | ||
30 | |||
31 | ngOnInit () { | ||
32 | this.descriptionChanged | ||
33 | .debounceTime(150) | ||
34 | .distinctUntilChanged() | ||
35 | .subscribe(() => this.updateDescriptionPreviews()) | ||
36 | |||
37 | this.descriptionChanged.next(this.description) | ||
38 | } | ||
39 | |||
40 | propagateChange = (_: any) => { /* empty */ } | ||
41 | |||
42 | writeValue (description: string) { | ||
43 | this.description = description | ||
44 | |||
45 | this.descriptionChanged.next(this.description) | ||
46 | } | ||
47 | |||
48 | registerOnChange (fn: (_: any) => void) { | ||
49 | this.propagateChange = fn | ||
50 | } | ||
51 | |||
52 | registerOnTouched () { | ||
53 | // Unused | ||
54 | } | ||
55 | |||
56 | onModelChange () { | ||
57 | this.propagateChange(this.description) | ||
58 | |||
59 | this.descriptionChanged.next(this.description) | ||
60 | } | ||
61 | |||
62 | private updateDescriptionPreviews () { | ||
63 | this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 })) | ||
64 | this.descriptionHTML = this.markdownService.markdownToHTML(this.description) | ||
65 | } | ||
66 | } | ||
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.scss b/client/src/app/videos/+video-edit/shared/video-edit.component.scss index 2d0bfc36e..d363499ce 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.scss | |||
@@ -43,6 +43,14 @@ | |||
43 | position: relative; | 43 | position: relative; |
44 | bottom: $button-height; | 44 | bottom: $button-height; |
45 | 45 | ||
46 | .message-submit { | ||
47 | display: inline-block; | ||
48 | margin-right: 25px; | ||
49 | |||
50 | color: #585858; | ||
51 | font-size: 15px; | ||
52 | } | ||
53 | |||
46 | .submit-button { | 54 | .submit-button { |
47 | @include peertube-button; | 55 | @include peertube-button; |
48 | @include orange-button; | 56 | @include orange-button; |
@@ -54,6 +62,7 @@ | |||
54 | background-color: inherit; | 62 | background-color: inherit; |
55 | border: none; | 63 | border: none; |
56 | padding: 0; | 64 | padding: 0; |
65 | outline: 0; | ||
57 | } | 66 | } |
58 | 67 | ||
59 | .icon.icon-validate { | 68 | .icon.icon-validate { |
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.module.ts b/client/src/app/videos/+video-edit/shared/video-edit.module.ts index c7ed8c351..ce106d82f 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.module.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.module.ts | |||
@@ -3,8 +3,9 @@ import { NgModule } from '@angular/core' | |||
3 | import { TagInputModule } from 'ngx-chips' | 3 | import { TagInputModule } from 'ngx-chips' |
4 | import { TabsModule } from 'ngx-bootstrap/tabs' | 4 | import { TabsModule } from 'ngx-bootstrap/tabs' |
5 | 5 | ||
6 | import { MarkdownService, VideoDescriptionComponent } from '../../shared' | 6 | import { MarkdownService } from '../../shared' |
7 | import { SharedModule } from '../../../shared' | 7 | import { SharedModule } from '../../../shared' |
8 | import { VideoDescriptionComponent } from './video-description.component' | ||
8 | import { VideoEditComponent } from './video-edit.component' | 9 | import { VideoEditComponent } from './video-edit.component' |
9 | 10 | ||
10 | @NgModule({ | 11 | @NgModule({ |
diff --git a/client/src/app/videos/+video-edit/video-add.component.html b/client/src/app/videos/+video-edit/video-add.component.html index 6883f8280..a6f2bf6f2 100644 --- a/client/src/app/videos/+video-edit/video-add.component.html +++ b/client/src/app/videos/+video-edit/video-add.component.html | |||
@@ -15,20 +15,23 @@ | |||
15 | </div> | 15 | </div> |
16 | 16 | ||
17 | <div class="form-group"> | 17 | <div class="form-group"> |
18 | <select [(ngModel)]="firstStepPrivacy"> | 18 | <select [(ngModel)]="firstStepPrivacyId"> |
19 | <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option> | 19 | <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option> |
20 | </select> | 20 | </select> |
21 | </div> | 21 | </div> |
22 | 22 | ||
23 | <div class="form-group"> | 23 | <div class="form-group"> |
24 | <select [(ngModel)]="firstStepChannel"> | 24 | <select [(ngModel)]="firstStepChannelId"> |
25 | <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option> | 25 | <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option> |
26 | </select> | 26 | </select> |
27 | </div> | 27 | </div> |
28 | </div> | 28 | </div> |
29 | </div> | 29 | </div> |
30 | 30 | ||
31 | <p-progressBar *ngIf="isUploadingVideo" [value]="videoUploadPercents"></p-progressBar> | 31 | <p-progressBar |
32 | *ngIf="isUploadingVideo" [value]="videoUploadPercents" | ||
33 | [ngClass]="{ processing: videoUploadPercents === 100 && videoUploaded === false }" | ||
34 | ></p-progressBar> | ||
32 | 35 | ||
33 | <!-- Hidden because we need to load the component --> | 36 | <!-- Hidden because we need to load the component --> |
34 | <form [hidden]="!isUploadingVideo" novalidate [formGroup]="form"> | 37 | <form [hidden]="!isUploadingVideo" novalidate [formGroup]="form"> |
@@ -37,10 +40,13 @@ | |||
37 | [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" | 40 | [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" |
38 | ></my-video-edit> | 41 | ></my-video-edit> |
39 | 42 | ||
43 | |||
40 | <div class="submit-container"> | 44 | <div class="submit-container"> |
41 | <div class="submit-button" [ngClass]="{ disabled: !form.valid }"> | 45 | <div *ngIf="videoUploaded === false" class="message-submit">Publish will be available when upload is finished</div> |
46 | |||
47 | <div class="submit-button" (click)="updateSecondStep()" [ngClass]="{ disabled: !form.valid || videoUploaded !== true }"> | ||
42 | <span class="icon icon-validate"></span> | 48 | <span class="icon icon-validate"></span> |
43 | <input type="button" value="Publish" (click)="upload()" /> | 49 | <input type="button" value="Publish" /> |
44 | </div> | 50 | </div> |
45 | </div> | 51 | </div> |
46 | </form> | 52 | </form> |
diff --git a/client/src/app/videos/+video-edit/video-add.component.scss b/client/src/app/videos/+video-edit/video-add.component.scss index dfdf7ff73..39673b4b7 100644 --- a/client/src/app/videos/+video-edit/video-add.component.scss +++ b/client/src/app/videos/+video-edit/video-add.component.scss | |||
@@ -18,6 +18,7 @@ | |||
18 | .icon.icon-upload { | 18 | .icon.icon-upload { |
19 | @include icon(90px); | 19 | @include icon(90px); |
20 | margin-bottom: 25px; | 20 | margin-bottom: 25px; |
21 | cursor: default; | ||
21 | 22 | ||
22 | background-image: url('../../../assets/images/video/upload.svg'); | 23 | background-image: url('../../../assets/images/video/upload.svg'); |
23 | } | 24 | } |
@@ -58,10 +59,9 @@ | |||
58 | } | 59 | } |
59 | 60 | ||
60 | p-progressBar { | 61 | p-progressBar { |
61 | margin-top: 50px; | ||
62 | margin-bottom: 40px; | ||
63 | |||
64 | /deep/ .ui-progressbar { | 62 | /deep/ .ui-progressbar { |
63 | margin-top: 25px !important; | ||
64 | margin-bottom: 40px !important; | ||
65 | font-size: 15px !important; | 65 | font-size: 15px !important; |
66 | color: #fff !important; | 66 | color: #fff !important; |
67 | height: 30px !important; | 67 | height: 30px !important; |
@@ -76,6 +76,19 @@ p-progressBar { | |||
76 | .ui-progressbar-label { | 76 | .ui-progressbar-label { |
77 | text-align: left; | 77 | text-align: left; |
78 | padding-left: 18px; | 78 | padding-left: 18px; |
79 | margin-top: 0 !important; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | &.processing { | ||
84 | /deep/ .ui-progressbar-label { | ||
85 | // Same color as background to hide "100%" | ||
86 | color: rgba(11, 204, 41, 0.16) !important; | ||
87 | |||
88 | &::before { | ||
89 | content: 'Processing...'; | ||
90 | color: #fff; | ||
91 | } | ||
79 | } | 92 | } |
80 | } | 93 | } |
81 | } | 94 | } |
diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts index 2409acfda..2bbc3de17 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts | |||
@@ -5,6 +5,7 @@ import { Router } from '@angular/router' | |||
5 | import { NotificationsService } from 'angular2-notifications' | 5 | import { NotificationsService } from 'angular2-notifications' |
6 | import { VideoService } from 'app/shared/video/video.service' | 6 | import { VideoService } from 'app/shared/video/video.service' |
7 | import { VideoCreate } from '../../../../../shared' | 7 | import { VideoCreate } from '../../../../../shared' |
8 | import { VideoPrivacy } from '../../../../../shared/models/videos' | ||
8 | import { AuthService, ServerService } from '../../core' | 9 | import { AuthService, ServerService } from '../../core' |
9 | import { FormReactive } from '../../shared' | 10 | import { FormReactive } from '../../shared' |
10 | import { ValidatorMessage } from '../../shared/forms/form-validators' | 11 | import { ValidatorMessage } from '../../shared/forms/form-validators' |
@@ -25,6 +26,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
25 | isUploadingVideo = false | 26 | isUploadingVideo = false |
26 | videoUploaded = false | 27 | videoUploaded = false |
27 | videoUploadPercents = 0 | 28 | videoUploadPercents = 0 |
29 | videoUploadedId = 0 | ||
28 | 30 | ||
29 | error: string = null | 31 | error: string = null |
30 | form: FormGroup | 32 | form: FormGroup |
@@ -33,8 +35,8 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
33 | 35 | ||
34 | userVideoChannels = [] | 36 | userVideoChannels = [] |
35 | videoPrivacies = [] | 37 | videoPrivacies = [] |
36 | firstStepPrivacy = 0 | 38 | firstStepPrivacyId = 0 |
37 | firstStepChannel = 0 | 39 | firstStepChannelId = 0 |
38 | 40 | ||
39 | constructor ( | 41 | constructor ( |
40 | private formBuilder: FormBuilder, | 42 | private formBuilder: FormBuilder, |
@@ -59,7 +61,9 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
59 | .subscribe( | 61 | .subscribe( |
60 | () => { | 62 | () => { |
61 | this.videoPrivacies = this.serverService.getVideoPrivacies() | 63 | this.videoPrivacies = this.serverService.getVideoPrivacies() |
62 | this.firstStepPrivacy = this.videoPrivacies[0].id | 64 | |
65 | // Public by default | ||
66 | this.firstStepPrivacyId = VideoPrivacy.PUBLIC | ||
63 | }) | 67 | }) |
64 | 68 | ||
65 | this.authService.userInformationLoaded | 69 | this.authService.userInformationLoaded |
@@ -72,7 +76,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
72 | if (Array.isArray(videoChannels) === false) return | 76 | if (Array.isArray(videoChannels) === false) return |
73 | 77 | ||
74 | this.userVideoChannels = videoChannels.map(v => ({ id: v.id, label: v.name })) | 78 | this.userVideoChannels = videoChannels.map(v => ({ id: v.id, label: v.name })) |
75 | this.firstStepChannel = this.userVideoChannels[0].id | 79 | this.firstStepChannelId = this.userVideoChannels[0].id |
76 | } | 80 | } |
77 | ) | 81 | ) |
78 | } | 82 | } |
@@ -89,14 +93,15 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
89 | 93 | ||
90 | uploadFirstStep () { | 94 | uploadFirstStep () { |
91 | const videofile = this.videofileInput.nativeElement.files[0] | 95 | const videofile = this.videofileInput.nativeElement.files[0] |
92 | const name = videofile.name | 96 | const name = videofile.name.replace(/\.[^/.]+$/, '') |
93 | const privacy = this.firstStepPrivacy.toString() | 97 | const privacy = this.firstStepPrivacyId.toString() |
94 | const nsfw = false | 98 | const nsfw = false |
95 | const channelId = this.firstStepChannel.toString() | 99 | const channelId = this.firstStepChannelId.toString() |
96 | 100 | ||
97 | const formData = new FormData() | 101 | const formData = new FormData() |
98 | formData.append('name', name) | 102 | formData.append('name', name) |
99 | formData.append('privacy', privacy.toString()) | 103 | // Put the video "private" -> we wait he validates the second step |
104 | formData.append('privacy', VideoPrivacy.PRIVATE.toString()) | ||
100 | formData.append('nsfw', '' + nsfw) | 105 | formData.append('nsfw', '' + nsfw) |
101 | formData.append('channelId', '' + channelId) | 106 | formData.append('channelId', '' + channelId) |
102 | formData.append('videofile', videofile) | 107 | formData.append('videofile', videofile) |
@@ -117,6 +122,8 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
117 | console.log('Video uploaded.') | 122 | console.log('Video uploaded.') |
118 | 123 | ||
119 | this.videoUploaded = true | 124 | this.videoUploaded = true |
125 | |||
126 | this.videoUploadedId = event.body.video.id | ||
120 | } | 127 | } |
121 | }, | 128 | }, |
122 | 129 | ||
@@ -133,13 +140,16 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
133 | return | 140 | return |
134 | } | 141 | } |
135 | 142 | ||
136 | const video = new VideoEdit(this.form.value) | 143 | const video = new VideoEdit() |
144 | video.patch(this.form.value) | ||
145 | video.channel = this.firstStepChannelId | ||
146 | video.id = this.videoUploadedId | ||
137 | 147 | ||
138 | this.videoService.updateVideo(video) | 148 | this.videoService.updateVideo(video) |
139 | .subscribe( | 149 | .subscribe( |
140 | () => { | 150 | () => { |
141 | this.notificationsService.success('Success', 'Video published.') | 151 | this.notificationsService.success('Success', 'Video published.') |
142 | this.router.navigate([ '/videos/watch', video.uuid ]) | 152 | this.router.navigate([ '/videos/watch', video.id ]) |
143 | }, | 153 | }, |
144 | 154 | ||
145 | err => { | 155 | err => { |
diff --git a/client/src/app/videos/+video-edit/video-update.component.html b/client/src/app/videos/+video-edit/video-update.component.html index 3163495bf..261b8a130 100644 --- a/client/src/app/videos/+video-edit/video-update.component.html +++ b/client/src/app/videos/+video-edit/video-update.component.html | |||
@@ -11,9 +11,9 @@ | |||
11 | ></my-video-edit> | 11 | ></my-video-edit> |
12 | 12 | ||
13 | <div class="submit-container"> | 13 | <div class="submit-container"> |
14 | <div class="submit-button" [ngClass]="{ disabled: !form.valid }"> | 14 | <div class="submit-button" (click)="update()" [ngClass]="{ disabled: !form.valid }"> |
15 | <span class="icon icon-validate"></span> | 15 | <span class="icon icon-validate"></span> |
16 | <input type="button" value="Update" (click)="update()" /> | 16 | <input type="button" value="Update" /> |
17 | </div> | 17 | </div> |
18 | </div> | 18 | </div> |
19 | </form> | 19 | </form> |