diff options
Diffstat (limited to 'client/src')
27 files changed, 219 insertions, 51 deletions
diff --git a/client/src/app/about/about.component.ts b/client/src/app/about/about.component.ts index 6a2e59be1..adad32b26 100644 --- a/client/src/app/about/about.component.ts +++ b/client/src/app/about/about.component.ts | |||
@@ -27,8 +27,8 @@ export class AboutComponent implements OnInit { | |||
27 | this.serverService.getAbout() | 27 | this.serverService.getAbout() |
28 | .subscribe( | 28 | .subscribe( |
29 | res => { | 29 | res => { |
30 | this.descriptionHTML = this.markdownService.markdownToHTML(res.instance.description) | 30 | this.descriptionHTML = this.markdownService.textMarkdownToHTML(res.instance.description) |
31 | this.termsHTML = this.markdownService.markdownToHTML(res.instance.terms) | 31 | this.termsHTML = this.markdownService.textMarkdownToHTML(res.instance.terms) |
32 | }, | 32 | }, |
33 | 33 | ||
34 | err => this.notificationsService.error('Error', err) | 34 | err => this.notificationsService.error('Error', err) |
diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss index 24c4f66c7..8e88bceff 100644 --- a/client/src/app/app.component.scss +++ b/client/src/app/app.component.scss | |||
@@ -49,7 +49,6 @@ | |||
49 | 49 | ||
50 | #peertube-title { | 50 | #peertube-title { |
51 | @include disable-default-a-behaviour; | 51 | @include disable-default-a-behaviour; |
52 | width: 100%; | ||
53 | font-size: 20px; | 52 | font-size: 20px; |
54 | font-weight: $font-bold; | 53 | font-weight: $font-bold; |
55 | color: inherit !important; | 54 | color: inherit !important; |
@@ -79,6 +78,10 @@ | |||
79 | display: none; | 78 | display: none; |
80 | } | 79 | } |
81 | } | 80 | } |
81 | |||
82 | @media screen and (max-width: 350px) { | ||
83 | flex: auto; | ||
84 | } | ||
82 | } | 85 | } |
83 | 86 | ||
84 | .header-right { | 87 | .header-right { |
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 220b104b7..3af33ba2b 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { GuardsCheckStart, NavigationEnd, Router } from '@angular/router' | 2 | import { GuardsCheckStart, Router } from '@angular/router' |
3 | import { AuthService, ServerService } from '@app/core' | 3 | import { AuthService, ServerService } from '@app/core' |
4 | import { isInSmallView } from '@app/shared/misc/utils' | 4 | import { isInSmallView } from '@app/shared/misc/utils' |
5 | 5 | ||
diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss index 63b724cba..d79e6274b 100644 --- a/client/src/app/header/header.component.scss +++ b/client/src/app/header/header.component.scss | |||
@@ -14,7 +14,7 @@ | |||
14 | width: calc(100% - 150px); | 14 | width: calc(100% - 150px); |
15 | } | 15 | } |
16 | 16 | ||
17 | @media screen and (max-width: 400px) { | 17 | @media screen and (max-width: 600px) { |
18 | width: calc(100% - 70px); | 18 | width: calc(100% - 70px); |
19 | } | 19 | } |
20 | } | 20 | } |
@@ -50,7 +50,7 @@ | |||
50 | margin-right: 6px; | 50 | margin-right: 6px; |
51 | } | 51 | } |
52 | 52 | ||
53 | @media screen and (max-width: 400px) { | 53 | @media screen and (max-width: 600px) { |
54 | margin-right: 10px; | 54 | margin-right: 10px; |
55 | padding: 0 10px; | 55 | padding: 0 10px; |
56 | 56 | ||
diff --git a/client/src/app/shared/forms/form-validators/video.ts b/client/src/app/shared/forms/form-validators/video.ts index 34a237a12..9ecbbbd60 100644 --- a/client/src/app/shared/forms/form-validators/video.ts +++ b/client/src/app/shared/forms/form-validators/video.ts | |||
@@ -44,10 +44,10 @@ export const VIDEO_CHANNEL = { | |||
44 | } | 44 | } |
45 | 45 | ||
46 | export const VIDEO_DESCRIPTION = { | 46 | export const VIDEO_DESCRIPTION = { |
47 | VALIDATORS: [ Validators.minLength(3), Validators.maxLength(3000) ], | 47 | VALIDATORS: [ Validators.minLength(3), Validators.maxLength(10000) ], |
48 | MESSAGES: { | 48 | MESSAGES: { |
49 | 'minlength': 'Video description must be at least 3 characters long.', | 49 | 'minlength': 'Video description must be at least 3 characters long.', |
50 | 'maxlength': 'Video description cannot be more than 3000 characters long.' | 50 | 'maxlength': 'Video description cannot be more than 10000 characters long.' |
51 | } | 51 | } |
52 | } | 52 | } |
53 | 53 | ||
@@ -58,3 +58,11 @@ export const VIDEO_TAGS = { | |||
58 | 'maxlength': 'A tag should be less than 30 characters long.' | 58 | 'maxlength': 'A tag should be less than 30 characters long.' |
59 | } | 59 | } |
60 | } | 60 | } |
61 | |||
62 | export const VIDEO_SUPPORT = { | ||
63 | VALIDATORS: [ Validators.minLength(3), Validators.maxLength(300) ], | ||
64 | MESSAGES: { | ||
65 | 'minlength': 'Video support must be at least 3 characters long.', | ||
66 | 'maxlength': 'Video support cannot be more than 300 characters long.' | ||
67 | } | ||
68 | } | ||
diff --git a/client/src/app/shared/forms/markdown-textarea.component.html b/client/src/app/shared/forms/markdown-textarea.component.html index e8c5ded5b..46a97b163 100644 --- a/client/src/app/shared/forms/markdown-textarea.component.html +++ b/client/src/app/shared/forms/markdown-textarea.component.html | |||
@@ -1,12 +1,12 @@ | |||
1 | <div class="root" [ngStyle]="{ 'flex-direction': flexDirection }"> | 1 | <div class="root" [ngStyle]="{ 'flex-direction': flexDirection }"> |
2 | <textarea | 2 | <textarea |
3 | [(ngModel)]="description" (ngModelChange)="onModelChange()" | 3 | [(ngModel)]="content" (ngModelChange)="onModelChange()" |
4 | [ngClass]="classes" [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }" | 4 | [ngClass]="classes" [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }" |
5 | id="description" name="description"> | 5 | id="description" name="description"> |
6 | </textarea> | 6 | </textarea> |
7 | 7 | ||
8 | <tabset *ngIf="arePreviewsDisplayed()" class="previews"> | 8 | <tabset *ngIf="arePreviewsDisplayed()" class="previews"> |
9 | <tab *ngIf="truncate !== undefined" heading="Truncated description preview" [innerHTML]="truncatedDescriptionHTML"></tab> | 9 | <tab *ngIf="truncate !== undefined" heading="Truncated preview" [innerHTML]="truncatedPreviewHTML"></tab> |
10 | <tab heading="Complete description preview" [innerHTML]="descriptionHTML"></tab> | 10 | <tab heading="Complete preview" [innerHTML]="previewHTML"></tab> |
11 | </tabset> | 11 | </tabset> |
12 | </div> | 12 | </div> |
diff --git a/client/src/app/shared/forms/markdown-textarea.component.scss b/client/src/app/shared/forms/markdown-textarea.component.scss index 82aff541d..6f7494621 100644 --- a/client/src/app/shared/forms/markdown-textarea.component.scss +++ b/client/src/app/shared/forms/markdown-textarea.component.scss | |||
@@ -22,6 +22,7 @@ | |||
22 | min-height: 75px; | 22 | min-height: 75px; |
23 | padding: 15px; | 23 | padding: 15px; |
24 | font-size: 15px; | 24 | font-size: 15px; |
25 | word-wrap: break-word; | ||
25 | } | 26 | } |
26 | } | 27 | } |
27 | } | 28 | } |
diff --git a/client/src/app/shared/forms/markdown-textarea.component.ts b/client/src/app/shared/forms/markdown-textarea.component.ts index 2eae1b27e..928a63b28 100644 --- a/client/src/app/shared/forms/markdown-textarea.component.ts +++ b/client/src/app/shared/forms/markdown-textarea.component.ts | |||
@@ -21,29 +21,30 @@ import truncate from 'lodash-es/truncate' | |||
21 | }) | 21 | }) |
22 | 22 | ||
23 | export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { | 23 | export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { |
24 | @Input() description = '' | 24 | @Input() content = '' |
25 | @Input() classes: string[] = [] | 25 | @Input() classes: string[] = [] |
26 | @Input() textareaWidth = '100%' | 26 | @Input() textareaWidth = '100%' |
27 | @Input() textareaHeight = '150px' | 27 | @Input() textareaHeight = '150px' |
28 | @Input() previewColumn = false | 28 | @Input() previewColumn = false |
29 | @Input() truncate: number | 29 | @Input() truncate: number |
30 | @Input() markdownType: 'text' | 'enhanced' = 'text' | ||
30 | 31 | ||
31 | textareaMarginRight = '0' | 32 | textareaMarginRight = '0' |
32 | flexDirection = 'column' | 33 | flexDirection = 'column' |
33 | truncatedDescriptionHTML = '' | 34 | truncatedPreviewHTML = '' |
34 | descriptionHTML = '' | 35 | previewHTML = '' |
35 | 36 | ||
36 | private descriptionChanged = new Subject<string>() | 37 | private contentChanged = new Subject<string>() |
37 | 38 | ||
38 | constructor (private markdownService: MarkdownService) {} | 39 | constructor (private markdownService: MarkdownService) {} |
39 | 40 | ||
40 | ngOnInit () { | 41 | ngOnInit () { |
41 | this.descriptionChanged | 42 | this.contentChanged |
42 | .debounceTime(150) | 43 | .debounceTime(150) |
43 | .distinctUntilChanged() | 44 | .distinctUntilChanged() |
44 | .subscribe(() => this.updateDescriptionPreviews()) | 45 | .subscribe(() => this.updatePreviews()) |
45 | 46 | ||
46 | this.descriptionChanged.next(this.description) | 47 | this.contentChanged.next(this.content) |
47 | 48 | ||
48 | if (this.previewColumn) { | 49 | if (this.previewColumn) { |
49 | this.flexDirection = 'row' | 50 | this.flexDirection = 'row' |
@@ -54,9 +55,9 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { | |||
54 | propagateChange = (_: any) => { /* empty */ } | 55 | propagateChange = (_: any) => { /* empty */ } |
55 | 56 | ||
56 | writeValue (description: string) { | 57 | writeValue (description: string) { |
57 | this.description = description | 58 | this.content = description |
58 | 59 | ||
59 | this.descriptionChanged.next(this.description) | 60 | this.contentChanged.next(this.content) |
60 | } | 61 | } |
61 | 62 | ||
62 | registerOnChange (fn: (_: any) => void) { | 63 | registerOnChange (fn: (_: any) => void) { |
@@ -68,19 +69,25 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { | |||
68 | } | 69 | } |
69 | 70 | ||
70 | onModelChange () { | 71 | onModelChange () { |
71 | this.propagateChange(this.description) | 72 | this.propagateChange(this.content) |
72 | 73 | ||
73 | this.descriptionChanged.next(this.description) | 74 | this.contentChanged.next(this.content) |
74 | } | 75 | } |
75 | 76 | ||
76 | arePreviewsDisplayed () { | 77 | arePreviewsDisplayed () { |
77 | return isInSmallView() === false | 78 | return isInSmallView() === false |
78 | } | 79 | } |
79 | 80 | ||
80 | private updateDescriptionPreviews () { | 81 | private updatePreviews () { |
81 | if (this.description === null || this.description === undefined) return | 82 | if (this.content === null || this.content === undefined) return |
82 | 83 | ||
83 | this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: this.truncate })) | 84 | this.truncatedPreviewHTML = this.markdownRender(truncate(this.content, { length: this.truncate })) |
84 | this.descriptionHTML = this.markdownService.markdownToHTML(this.description) | 85 | this.previewHTML = this.markdownRender(this.content) |
86 | } | ||
87 | |||
88 | private markdownRender (text: string) { | ||
89 | if (this.markdownType === 'text') return this.markdownService.textMarkdownToHTML(text) | ||
90 | |||
91 | return this.markdownService.enhancedMarkdownToHTML(text) | ||
85 | } | 92 | } |
86 | } | 93 | } |
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts index c746bfd66..4e4f64c7b 100644 --- a/client/src/app/shared/video/video-details.model.ts +++ b/client/src/app/shared/video/video-details.model.ts | |||
@@ -57,6 +57,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { | |||
57 | this.channel = hash.channel | 57 | this.channel = hash.channel |
58 | this.account = hash.account | 58 | this.account = hash.account |
59 | this.tags = hash.tags | 59 | this.tags = hash.tags |
60 | this.support = hash.support | ||
60 | this.commentsEnabled = hash.commentsEnabled | 61 | this.commentsEnabled = hash.commentsEnabled |
61 | 62 | ||
62 | this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100 | 63 | this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100 |
diff --git a/client/src/app/shared/video/video-edit.model.ts b/client/src/app/shared/video/video-edit.model.ts index c39252f46..a8bbb63eb 100644 --- a/client/src/app/shared/video/video-edit.model.ts +++ b/client/src/app/shared/video/video-edit.model.ts | |||
@@ -12,6 +12,7 @@ export class VideoEdit { | |||
12 | commentsEnabled: boolean | 12 | commentsEnabled: boolean |
13 | channel: number | 13 | channel: number |
14 | privacy: VideoPrivacy | 14 | privacy: VideoPrivacy |
15 | support: string | ||
15 | thumbnailfile?: any | 16 | thumbnailfile?: any |
16 | previewfile?: any | 17 | previewfile?: any |
17 | thumbnailUrl: string | 18 | thumbnailUrl: string |
@@ -33,6 +34,7 @@ export class VideoEdit { | |||
33 | this.commentsEnabled = videoDetails.commentsEnabled | 34 | this.commentsEnabled = videoDetails.commentsEnabled |
34 | this.channel = videoDetails.channel.id | 35 | this.channel = videoDetails.channel.id |
35 | this.privacy = videoDetails.privacy | 36 | this.privacy = videoDetails.privacy |
37 | this.support = videoDetails.support | ||
36 | this.thumbnailUrl = videoDetails.thumbnailUrl | 38 | this.thumbnailUrl = videoDetails.thumbnailUrl |
37 | this.previewUrl = videoDetails.previewUrl | 39 | this.previewUrl = videoDetails.previewUrl |
38 | } | 40 | } |
@@ -50,6 +52,7 @@ export class VideoEdit { | |||
50 | licence: this.licence, | 52 | licence: this.licence, |
51 | language: this.language, | 53 | language: this.language, |
52 | description: this.description, | 54 | description: this.description, |
55 | support: this.support, | ||
53 | name: this.name, | 56 | name: this.name, |
54 | tags: this.tags, | 57 | tags: this.tags, |
55 | nsfw: this.nsfw, | 58 | nsfw: this.nsfw, |
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index 2e7138cd1..99da14779 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts | |||
@@ -62,6 +62,7 @@ export class VideoService { | |||
62 | tags: video.tags, | 62 | tags: video.tags, |
63 | nsfw: video.nsfw, | 63 | nsfw: video.nsfw, |
64 | commentsEnabled: video.commentsEnabled, | 64 | commentsEnabled: video.commentsEnabled, |
65 | support: video.support, | ||
65 | thumbnailfile: video.thumbnailfile, | 66 | thumbnailfile: video.thumbnailfile, |
66 | previewfile: video.previewfile | 67 | previewfile: video.previewfile |
67 | } | 68 | } |
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html index 899249778..bf2204013 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html | |||
@@ -111,7 +111,7 @@ | |||
111 | </tab> | 111 | </tab> |
112 | 112 | ||
113 | <tab heading="Advanced settings"> | 113 | <tab heading="Advanced settings"> |
114 | <div class="col-md-12"> | 114 | <div class="col-md-12 advanced-settings"> |
115 | <div class="form-group"> | 115 | <div class="form-group"> |
116 | <my-video-image | 116 | <my-video-image |
117 | inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile" | 117 | inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile" |
@@ -125,6 +125,17 @@ | |||
125 | previewWidth="360px" previewHeight="200px" | 125 | previewWidth="360px" previewHeight="200px" |
126 | ></my-video-image> | 126 | ></my-video-image> |
127 | </div> | 127 | </div> |
128 | |||
129 | <div class="form-group"> | ||
130 | <label for="support">Support (markdown)</label> | ||
131 | <my-markdown-textarea | ||
132 | id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced" | ||
133 | [classes]="{ 'input-error': formErrors['support'] }" | ||
134 | ></my-markdown-textarea> | ||
135 | <div *ngIf="formErrors.support" class="form-error"> | ||
136 | {{ formErrors.support }} | ||
137 | </div> | ||
138 | </div> | ||
128 | </div> | 139 | </div> |
129 | </tab> | 140 | </tab> |
130 | 141 | ||
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 f78336fa8..1317f7426 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 | |||
@@ -59,6 +59,10 @@ | |||
59 | padding: 0 15px !important; | 59 | padding: 0 15px !important; |
60 | } | 60 | } |
61 | } | 61 | } |
62 | |||
63 | .advanced-settings .form-group { | ||
64 | margin-bottom: 20px; | ||
65 | } | ||
62 | } | 66 | } |
63 | 67 | ||
64 | .submit-container { | 68 | .submit-container { |
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.ts b/client/src/app/videos/+video-edit/shared/video-edit.component.ts index 85e5cc3f5..c26906551 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Component, Input, OnInit } from '@angular/core' | 1 | import { Component, Input, OnInit } from '@angular/core' |
2 | import { FormBuilder, FormControl, FormGroup } from '@angular/forms' | 2 | import { FormBuilder, FormControl, FormGroup } from '@angular/forms' |
3 | import { ActivatedRoute, Router } from '@angular/router' | 3 | import { ActivatedRoute, Router } from '@angular/router' |
4 | import { VIDEO_IMAGE } from '@app/shared' | 4 | import { VIDEO_IMAGE, VIDEO_SUPPORT } from '@app/shared' |
5 | import { NotificationsService } from 'angular2-notifications' | 5 | import { NotificationsService } from 'angular2-notifications' |
6 | import 'rxjs/add/observable/forkJoin' | 6 | import 'rxjs/add/observable/forkJoin' |
7 | import { ServerService } from '../../../core/server' | 7 | import { ServerService } from '../../../core/server' |
@@ -60,6 +60,7 @@ export class VideoEditComponent implements OnInit { | |||
60 | this.formErrors['description'] = '' | 60 | this.formErrors['description'] = '' |
61 | this.formErrors['thumbnailfile'] = '' | 61 | this.formErrors['thumbnailfile'] = '' |
62 | this.formErrors['previewfile'] = '' | 62 | this.formErrors['previewfile'] = '' |
63 | this.formErrors['support'] = '' | ||
63 | 64 | ||
64 | this.validationMessages['name'] = VIDEO_NAME.MESSAGES | 65 | this.validationMessages['name'] = VIDEO_NAME.MESSAGES |
65 | this.validationMessages['privacy'] = VIDEO_PRIVACY.MESSAGES | 66 | this.validationMessages['privacy'] = VIDEO_PRIVACY.MESSAGES |
@@ -70,6 +71,7 @@ export class VideoEditComponent implements OnInit { | |||
70 | this.validationMessages['description'] = VIDEO_DESCRIPTION.MESSAGES | 71 | this.validationMessages['description'] = VIDEO_DESCRIPTION.MESSAGES |
71 | this.validationMessages['thumbnailfile'] = VIDEO_IMAGE.MESSAGES | 72 | this.validationMessages['thumbnailfile'] = VIDEO_IMAGE.MESSAGES |
72 | this.validationMessages['previewfile'] = VIDEO_IMAGE.MESSAGES | 73 | this.validationMessages['previewfile'] = VIDEO_IMAGE.MESSAGES |
74 | this.validationMessages['support'] = VIDEO_SUPPORT.MESSAGES | ||
73 | 75 | ||
74 | this.form.addControl('name', new FormControl('', VIDEO_NAME.VALIDATORS)) | 76 | this.form.addControl('name', new FormControl('', VIDEO_NAME.VALIDATORS)) |
75 | this.form.addControl('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS)) | 77 | this.form.addControl('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS)) |
@@ -83,6 +85,7 @@ export class VideoEditComponent implements OnInit { | |||
83 | this.form.addControl('tags', new FormControl('')) | 85 | this.form.addControl('tags', new FormControl('')) |
84 | this.form.addControl('thumbnailfile', new FormControl('')) | 86 | this.form.addControl('thumbnailfile', new FormControl('')) |
85 | this.form.addControl('previewfile', new FormControl('')) | 87 | this.form.addControl('previewfile', new FormControl('')) |
88 | this.form.addControl('support', new FormControl('')) | ||
86 | } | 89 | } |
87 | 90 | ||
88 | ngOnInit () { | 91 | ngOnInit () { |
diff --git a/client/src/app/videos/+video-edit/shared/video-image.component.scss b/client/src/app/videos/+video-edit/shared/video-image.component.scss index c44d00b3c..98313536e 100644 --- a/client/src/app/videos/+video-edit/shared/video-image.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-image.component.scss | |||
@@ -2,7 +2,7 @@ | |||
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .root { | 4 | .root { |
5 | height: 150px; | 5 | height: auto; |
6 | display: flex; | 6 | display: flex; |
7 | align-items: center; | 7 | align-items: center; |
8 | 8 | ||
diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts index de34555ab..0ef3c0259 100644 --- a/client/src/app/videos/+video-edit/video-update.component.ts +++ b/client/src/app/videos/+video-edit/video-update.component.ts | |||
@@ -61,8 +61,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
61 | .switchMap(video => { | 61 | .switchMap(video => { |
62 | return this.videoService | 62 | return this.videoService |
63 | .loadCompleteDescription(video.descriptionPath) | 63 | .loadCompleteDescription(video.descriptionPath) |
64 | .do(description => video.description = description) | 64 | .map(description => Object.assign(video, { description })) |
65 | .map(() => video) | ||
66 | }) | 65 | }) |
67 | .subscribe( | 66 | .subscribe( |
68 | video => { | 67 | video => { |
diff --git a/client/src/app/videos/+video-watch/modal/video-support.component.html b/client/src/app/videos/+video-watch/modal/video-support.component.html new file mode 100644 index 000000000..16ad9502a --- /dev/null +++ b/client/src/app/videos/+video-watch/modal/video-support.component.html | |||
@@ -0,0 +1,22 @@ | |||
1 | <div bsModal #modal="bs-modal" class="modal" tabindex="-1"> | ||
2 | <div class="modal-dialog"> | ||
3 | <div class="modal-content"> | ||
4 | |||
5 | <div class="modal-header"> | ||
6 | <span class="close" aria-hidden="true" (click)="hide()"></span> | ||
7 | <h4 class="modal-title">Support</h4> | ||
8 | </div> | ||
9 | |||
10 | <div class="modal-body"> | ||
11 | |||
12 | <div [innerHTML]="videoHTMLSupport"></div> | ||
13 | |||
14 | <div class="form-group inputs"> | ||
15 | <span class="action-button action-button-cancel" (click)="hide()"> | ||
16 | Cancel | ||
17 | </span> | ||
18 | </div> | ||
19 | </div> | ||
20 | </div> | ||
21 | </div> | ||
22 | </div> | ||
diff --git a/client/src/app/videos/+video-watch/modal/video-support.component.scss b/client/src/app/videos/+video-watch/modal/video-support.component.scss new file mode 100644 index 000000000..184e09027 --- /dev/null +++ b/client/src/app/videos/+video-watch/modal/video-support.component.scss | |||
@@ -0,0 +1,3 @@ | |||
1 | .action-button-cancel { | ||
2 | margin-right: 0 !important; | ||
3 | } | ||
diff --git a/client/src/app/videos/+video-watch/modal/video-support.component.ts b/client/src/app/videos/+video-watch/modal/video-support.component.ts new file mode 100644 index 000000000..f805215b9 --- /dev/null +++ b/client/src/app/videos/+video-watch/modal/video-support.component.ts | |||
@@ -0,0 +1,36 @@ | |||
1 | import { Component, Input, ViewChild } from '@angular/core' | ||
2 | import { MarkdownService } from '@app/videos/shared' | ||
3 | |||
4 | import { ModalDirective } from 'ngx-bootstrap/modal' | ||
5 | import { VideoDetails } from '../../../shared/video/video-details.model' | ||
6 | |||
7 | @Component({ | ||
8 | selector: 'my-video-support', | ||
9 | templateUrl: './video-support.component.html', | ||
10 | styleUrls: [ './video-support.component.scss' ] | ||
11 | }) | ||
12 | export class VideoSupportComponent { | ||
13 | @Input() video: VideoDetails = null | ||
14 | |||
15 | @ViewChild('modal') modal: ModalDirective | ||
16 | |||
17 | videoHTMLSupport = '' | ||
18 | |||
19 | constructor (private markdownService: MarkdownService) { | ||
20 | // empty | ||
21 | } | ||
22 | |||
23 | show () { | ||
24 | this.modal.show() | ||
25 | |||
26 | if (this.video.support) { | ||
27 | this.videoHTMLSupport = this.markdownService.enhancedMarkdownToHTML(this.video.support) | ||
28 | } else { | ||
29 | this.videoHTMLSupport = '' | ||
30 | } | ||
31 | } | ||
32 | |||
33 | hide () { | ||
34 | this.modal.hide() | ||
35 | } | ||
36 | } | ||
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 8c173d6b3..b8ec048b2 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html | |||
@@ -44,9 +44,14 @@ | |||
44 | <span class="icon icon-dislike" title="Dislike this video"></span> | 44 | <span class="icon icon-dislike" title="Dislike this video"></span> |
45 | </div> | 45 | </div> |
46 | 46 | ||
47 | <div (click)="showSupportModal()" class="action-button action-button-support"> | ||
48 | <span class="icon icon-support"></span> | ||
49 | <span class="icon-text">Support</span> | ||
50 | </div> | ||
51 | |||
47 | <div (click)="showShareModal()" class="action-button action-button-share"> | 52 | <div (click)="showShareModal()" class="action-button action-button-share"> |
48 | <span class="icon icon-share"></span> | 53 | <span class="icon icon-share"></span> |
49 | Share | 54 | <span class="icon-text">Share</span> |
50 | </div> | 55 | </div> |
51 | 56 | ||
52 | <div class="action-more" dropdown dropup="true" placement="right"> | 57 | <div class="action-more" dropdown dropup="true" placement="right"> |
@@ -175,6 +180,7 @@ | |||
175 | </div> | 180 | </div> |
176 | 181 | ||
177 | <ng-template [ngIf]="video !== null"> | 182 | <ng-template [ngIf]="video !== null"> |
183 | <my-video-support #videoSupportModal [video]="video"></my-video-support> | ||
178 | <my-video-share #videoShareModal [video]="video"></my-video-share> | 184 | <my-video-share #videoShareModal [video]="video"></my-video-share> |
179 | <my-video-download #videoDownloadModal [video]="video"></my-video-download> | 185 | <my-video-download #videoDownloadModal [video]="video"></my-video-download> |
180 | <my-video-report #videoReportModal [video]="video"></my-video-report> | 186 | <my-video-report #videoReportModal [video]="video"></my-video-report> |
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 bc737ccd5..eb701e0ab 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss | |||
@@ -99,6 +99,7 @@ | |||
99 | font-weight: $font-semibold; | 99 | font-weight: $font-semibold; |
100 | display: inline-block; | 100 | display: inline-block; |
101 | padding: 0 10px 0 10px; | 101 | padding: 0 10px 0 10px; |
102 | white-space: nowrap; | ||
102 | 103 | ||
103 | .icon { | 104 | .icon { |
104 | @include icon(21px); | 105 | @include icon(21px); |
@@ -114,6 +115,10 @@ | |||
114 | background-image: url('../../../assets/images/video/dislike-grey.svg'); | 115 | background-image: url('../../../assets/images/video/dislike-grey.svg'); |
115 | } | 116 | } |
116 | 117 | ||
118 | &.icon-support { | ||
119 | background-image: url('../../../assets/images/video/heart.svg'); | ||
120 | } | ||
121 | |||
117 | &.icon-share { | 122 | &.icon-share { |
118 | background-image: url('../../../assets/images/video/share.svg'); | 123 | background-image: url('../../../assets/images/video/share.svg'); |
119 | } | 124 | } |
@@ -249,11 +254,7 @@ | |||
249 | } | 254 | } |
250 | 255 | ||
251 | 256 | ||
252 | @media screen and (max-width: 1300px) { | 257 | @media screen and (max-width: 1600px) { |
253 | .other-videos { | ||
254 | display: none; | ||
255 | } | ||
256 | |||
257 | .video-bottom { | 258 | .video-bottom { |
258 | .video-info { | 259 | .video-info { |
259 | margin-right: 0; | 260 | margin-right: 0; |
@@ -288,6 +289,12 @@ | |||
288 | } | 289 | } |
289 | } | 290 | } |
290 | 291 | ||
292 | @media screen and (max-width: 1200px) { | ||
293 | .other-videos { | ||
294 | display: none; | ||
295 | } | ||
296 | } | ||
297 | |||
291 | @media screen and (max-width: 600px) { | 298 | @media screen and (max-width: 600px) { |
292 | .video-bottom { | 299 | .video-bottom { |
293 | margin: 20px 0 0 0; | 300 | margin: 20px 0 0 0; |
@@ -304,3 +311,9 @@ | |||
304 | } | 311 | } |
305 | } | 312 | } |
306 | } | 313 | } |
314 | |||
315 | @media screen and (max-width: 450px) { | ||
316 | .video-bottom .action-button .icon-text { | ||
317 | display: none !important; | ||
318 | } | ||
319 | } | ||
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 553eed341..6b118b1de 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' |
2 | import { ActivatedRoute, Router } from '@angular/router' | 2 | import { ActivatedRoute, Router } from '@angular/router' |
3 | import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component' | ||
3 | import { MetaService } from '@ngx-meta/core' | 4 | import { MetaService } from '@ngx-meta/core' |
4 | import { NotificationsService } from 'angular2-notifications' | 5 | import { NotificationsService } from 'angular2-notifications' |
5 | import { Observable } from 'rxjs/Observable' | 6 | import { Observable } from 'rxjs/Observable' |
@@ -28,6 +29,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
28 | @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent | 29 | @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent |
29 | @ViewChild('videoShareModal') videoShareModal: VideoShareComponent | 30 | @ViewChild('videoShareModal') videoShareModal: VideoShareComponent |
30 | @ViewChild('videoReportModal') videoReportModal: VideoReportComponent | 31 | @ViewChild('videoReportModal') videoReportModal: VideoReportComponent |
32 | @ViewChild('videoSupportModal') videoSupportModal: VideoSupportComponent | ||
31 | 33 | ||
32 | otherVideosDisplayed: Video[] = [] | 34 | otherVideosDisplayed: Video[] = [] |
33 | 35 | ||
@@ -189,6 +191,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
189 | this.videoReportModal.show() | 191 | this.videoReportModal.show() |
190 | } | 192 | } |
191 | 193 | ||
194 | showSupportModal () { | ||
195 | this.videoSupportModal.show() | ||
196 | } | ||
197 | |||
192 | showShareModal () { | 198 | showShareModal () { |
193 | this.videoShareModal.show() | 199 | this.videoShareModal.show() |
194 | } | 200 | } |
@@ -264,7 +270,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
264 | return | 270 | return |
265 | } | 271 | } |
266 | 272 | ||
267 | this.videoHTMLDescription = this.markdownService.markdownToHTML(this.video.description) | 273 | this.videoHTMLDescription = this.markdownService.textMarkdownToHTML(this.video.description) |
268 | } | 274 | } |
269 | 275 | ||
270 | private setVideoLikesBarTooltipText () { | 276 | private setVideoLikesBarTooltipText () { |
diff --git a/client/src/app/videos/+video-watch/video-watch.module.ts b/client/src/app/videos/+video-watch/video-watch.module.ts index 085a9ec5a..6a22c36d9 100644 --- a/client/src/app/videos/+video-watch/video-watch.module.ts +++ b/client/src/app/videos/+video-watch/video-watch.module.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { NgModule } from '@angular/core' | 1 | import { NgModule } from '@angular/core' |
2 | import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component' | ||
2 | import { TooltipModule } from 'ngx-bootstrap/tooltip' | 3 | import { TooltipModule } from 'ngx-bootstrap/tooltip' |
3 | import { ClipboardModule } from 'ngx-clipboard' | 4 | import { ClipboardModule } from 'ngx-clipboard' |
4 | import { SharedModule } from '../../shared' | 5 | import { SharedModule } from '../../shared' |
@@ -29,6 +30,7 @@ import { VideoWatchComponent } from './video-watch.component' | |||
29 | VideoDownloadComponent, | 30 | VideoDownloadComponent, |
30 | VideoShareComponent, | 31 | VideoShareComponent, |
31 | VideoReportComponent, | 32 | VideoReportComponent, |
33 | VideoSupportComponent, | ||
32 | VideoCommentsComponent, | 34 | VideoCommentsComponent, |
33 | VideoCommentAddComponent, | 35 | VideoCommentAddComponent, |
34 | VideoCommentComponent | 36 | VideoCommentComponent |
diff --git a/client/src/app/videos/shared/markdown.service.ts b/client/src/app/videos/shared/markdown.service.ts index a275446eb..bd100f092 100644 --- a/client/src/app/videos/shared/markdown.service.ts +++ b/client/src/app/videos/shared/markdown.service.ts | |||
@@ -4,33 +4,51 @@ import * as MarkdownIt from 'markdown-it' | |||
4 | 4 | ||
5 | @Injectable() | 5 | @Injectable() |
6 | export class MarkdownService { | 6 | export class MarkdownService { |
7 | private markdownIt: MarkdownIt.MarkdownIt | 7 | private textMarkdownIt: MarkdownIt.MarkdownIt |
8 | private linkifier: MarkdownIt.MarkdownIt | 8 | private linkifier: MarkdownIt.MarkdownIt |
9 | private enhancedMarkdownIt: MarkdownIt.MarkdownIt | ||
9 | 10 | ||
10 | constructor () { | 11 | constructor () { |
11 | this.markdownIt = new MarkdownIt('zero', { linkify: true, breaks: true }) | 12 | this.textMarkdownIt = new MarkdownIt('zero', { linkify: true, breaks: true }) |
12 | .enable('linkify') | 13 | .enable('linkify') |
13 | .enable('autolink') | 14 | .enable('autolink') |
14 | .enable('emphasis') | 15 | .enable('emphasis') |
15 | .enable('link') | 16 | .enable('link') |
16 | .enable('newline') | 17 | .enable('newline') |
17 | .enable('list') | 18 | .enable('list') |
18 | this.setTargetToLinks(this.markdownIt) | 19 | this.setTargetToLinks(this.textMarkdownIt) |
20 | |||
21 | this.enhancedMarkdownIt = new MarkdownIt('zero', { linkify: true, breaks: true }) | ||
22 | .enable('linkify') | ||
23 | .enable('autolink') | ||
24 | .enable('emphasis') | ||
25 | .enable('link') | ||
26 | .enable('newline') | ||
27 | .enable('list') | ||
28 | .enable('image') | ||
29 | this.setTargetToLinks(this.enhancedMarkdownIt) | ||
19 | 30 | ||
20 | this.linkifier = new MarkdownIt('zero', { linkify: true }) | 31 | this.linkifier = new MarkdownIt('zero', { linkify: true }) |
21 | .enable('linkify') | 32 | .enable('linkify') |
22 | this.setTargetToLinks(this.linkifier) | 33 | this.setTargetToLinks(this.linkifier) |
23 | } | 34 | } |
24 | 35 | ||
25 | markdownToHTML (markdown: string) { | 36 | textMarkdownToHTML (markdown: string) { |
26 | const html = this.markdownIt.render(markdown) | 37 | const html = this.textMarkdownIt.render(markdown) |
27 | 38 | ||
28 | // Avoid linkify truncated links | 39 | return this.avoidTruncatedLinks(html) |
29 | return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...') | 40 | } |
41 | |||
42 | enhancedMarkdownToHTML (markdown: string) { | ||
43 | const html = this.enhancedMarkdownIt.render(markdown) | ||
44 | |||
45 | return this.avoidTruncatedLinks(html) | ||
30 | } | 46 | } |
31 | 47 | ||
32 | linkify (text: string) { | 48 | linkify (text: string) { |
33 | return this.linkifier.render(text) | 49 | const html = this.linkifier.render(text) |
50 | |||
51 | return this.avoidTruncatedLinks(html) | ||
34 | } | 52 | } |
35 | 53 | ||
36 | private setTargetToLinks (markdownIt: MarkdownIt.MarkdownIt) { | 54 | private setTargetToLinks (markdownIt: MarkdownIt.MarkdownIt) { |
@@ -53,4 +71,8 @@ export class MarkdownService { | |||
53 | return defaultRender(tokens, idx, options, env, self) | 71 | return defaultRender(tokens, idx, options, env, self) |
54 | } | 72 | } |
55 | } | 73 | } |
74 | |||
75 | private avoidTruncatedLinks (html) { | ||
76 | return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...') | ||
77 | } | ||
56 | } | 78 | } |
diff --git a/client/src/assets/images/video/heart.svg b/client/src/assets/images/video/heart.svg new file mode 100644 index 000000000..5d64aee0f --- /dev/null +++ b/client/src/assets/images/video/heart.svg | |||
@@ -0,0 +1,13 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | ||
3 | <defs></defs> | ||
4 | <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | ||
5 | <g id="Artboard-4" transform="translate(-48.000000, -1046.000000)" fill-rule="nonzero" fill="#585858"> | ||
6 | <g id="Extras" transform="translate(48.000000, 1046.000000)"> | ||
7 | <g id="heart"> | ||
8 | <path d="M12.0174466,21 L20.9041801,11.3556763 C22.6291961,9.13778099 22.2795957,5.90145416 20.1233257,4.12713796 C17.9670557,2.35282175 14.8206518,2.71241362 13.0956358,4.93030888 L12.0174465,6.5 L10.9043642,4.93030888 C9.17934824,2.71241362 6.0329443,2.35282175 3.87667432,4.12713796 C1.72040435,5.90145416 1.37080391,9.13778099 3.09581989,11.3556763 L12.0174466,21 Z"></path> | ||
9 | </g> | ||
10 | </g> | ||
11 | </g> | ||
12 | </g> | ||
13 | </svg> | ||
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 80dd3408f..f04aef85d 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss | |||
@@ -24,6 +24,10 @@ body { | |||
24 | color: #000; | 24 | color: #000; |
25 | } | 25 | } |
26 | 26 | ||
27 | strong { | ||
28 | font-weight: $font-semibold; | ||
29 | } | ||
30 | |||
27 | input.readonly { | 31 | input.readonly { |
28 | /* Force blank on readonly inputs */ | 32 | /* Force blank on readonly inputs */ |
29 | background-color: #fff !important; | 33 | background-color: #fff !important; |
diff --git a/client/src/sass/video-js-custom.scss b/client/src/sass/video-js-custom.scss index 209fb1683..ee6b9219b 100644 --- a/client/src/sass/video-js-custom.scss +++ b/client/src/sass/video-js-custom.scss | |||
@@ -24,7 +24,7 @@ $control-bar-height: 34px; | |||
24 | 24 | ||
25 | .vjs-big-play-button { | 25 | .vjs-big-play-button { |
26 | outline: 0; | 26 | outline: 0; |
27 | font-size: 7em; | 27 | font-size: 6em; |
28 | 28 | ||
29 | $big-play-width: 1.5em; | 29 | $big-play-width: 1.5em; |
30 | $big-play-height: 1em; | 30 | $big-play-height: 1em; |
@@ -340,7 +340,7 @@ $control-bar-height: 34px; | |||
340 | 340 | ||
341 | @media screen and (max-width: 550px) { | 341 | @media screen and (max-width: 550px) { |
342 | .vjs-big-play-button { | 342 | .vjs-big-play-button { |
343 | font-size: 6.5em; | 343 | font-size: 5em; |
344 | } | 344 | } |
345 | 345 | ||
346 | .vjs-webtorrent { | 346 | .vjs-webtorrent { |
@@ -358,7 +358,7 @@ $control-bar-height: 34px; | |||
358 | } | 358 | } |
359 | 359 | ||
360 | .vjs-big-play-button { | 360 | .vjs-big-play-button { |
361 | font-size: 5em; | 361 | font-size: 4em; |
362 | } | 362 | } |
363 | 363 | ||
364 | .vjs-volume-control { | 364 | .vjs-volume-control { |