diff options
author | Chocobozzz <me@florianbigard.com> | 2018-01-31 16:42:40 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-01-31 16:42:40 +0100 |
commit | 66b16cafb380012d3eca14e524d86f2450e04069 (patch) | |
tree | 02a6d2327e2499dbe921c22a03b8a9d741773dd6 /client/src | |
parent | 81ebea48bfba2d81e62dd7a0f01a0cadf41d2607 (diff) | |
download | PeerTube-66b16cafb380012d3eca14e524d86f2450e04069.tar.gz PeerTube-66b16cafb380012d3eca14e524d86f2450e04069.tar.zst PeerTube-66b16cafb380012d3eca14e524d86f2450e04069.zip |
Add new name/terms/description config options
Diffstat (limited to 'client/src')
13 files changed, 156 insertions, 70 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index c568a43b4..0fe2aa203 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html | |||
@@ -2,6 +2,41 @@ | |||
2 | 2 | ||
3 | <form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> | 3 | <form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> |
4 | 4 | ||
5 | <div class="inner-form-title">Instance</div> | ||
6 | |||
7 | <div class="form-group"> | ||
8 | <label for="instanceName">Name</label> | ||
9 | <input | ||
10 | type="text" id="instanceName" | ||
11 | formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }" | ||
12 | > | ||
13 | <div *ngIf="formErrors.instanceName" class="form-error"> | ||
14 | {{ formErrors.instanceName }} | ||
15 | </div> | ||
16 | </div> | ||
17 | |||
18 | <div class="form-group"> | ||
19 | <label for="instanceDescription">Description (markdown)</label> | ||
20 | <my-markdown-textarea | ||
21 | id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true" | ||
22 | [classes]="{ 'input-error': formErrors['instanceDescription'] }" | ||
23 | ></my-markdown-textarea> | ||
24 | <div *ngIf="formErrors.instanceDescription" class="form-error"> | ||
25 | {{ formErrors.instanceDescription }} | ||
26 | </div> | ||
27 | </div> | ||
28 | |||
29 | <div class="form-group"> | ||
30 | <label for="instanceTerms">Terms (markdown)</label> | ||
31 | <my-markdown-textarea | ||
32 | id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true" | ||
33 | [ngClass]="{ 'input-error': formErrors['instanceTerms'] }" | ||
34 | ></my-markdown-textarea> | ||
35 | <div *ngIf="formErrors.instanceTerms" class="form-error"> | ||
36 | {{ formErrors.instanceTerms }} | ||
37 | </div> | ||
38 | </div> | ||
39 | |||
5 | <div class="inner-form-title">Cache</div> | 40 | <div class="inner-form-title">Cache</div> |
6 | 41 | ||
7 | <div class="form-group"> | 42 | <div class="form-group"> |
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 1b3522786..cd8c926f7 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 | |||
@@ -4,7 +4,13 @@ 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 { ServerService } from '@app/core/server/server.service' | 5 | import { ServerService } from '@app/core/server/server.service' |
6 | import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared' | 6 | import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared' |
7 | import { ADMIN_EMAIL, CACHE_PREVIEWS_SIZE, SIGNUP_LIMIT, TRANSCODING_THREADS } from '@app/shared/forms/form-validators/custom-config' | 7 | import { |
8 | ADMIN_EMAIL, | ||
9 | CACHE_PREVIEWS_SIZE, | ||
10 | INSTANCE_NAME, | ||
11 | SIGNUP_LIMIT, | ||
12 | TRANSCODING_THREADS | ||
13 | } from '@app/shared/forms/form-validators/custom-config' | ||
8 | import { NotificationsService } from 'angular2-notifications' | 14 | import { NotificationsService } from 'angular2-notifications' |
9 | import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model' | 15 | import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model' |
10 | 16 | ||
@@ -36,6 +42,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
36 | 42 | ||
37 | form: FormGroup | 43 | form: FormGroup |
38 | formErrors = { | 44 | formErrors = { |
45 | instanceName: '', | ||
46 | instanceDescription: '', | ||
47 | instanceTerms: '', | ||
39 | cachePreviewsSize: '', | 48 | cachePreviewsSize: '', |
40 | signupLimit: '', | 49 | signupLimit: '', |
41 | adminEmail: '', | 50 | adminEmail: '', |
@@ -43,6 +52,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
43 | transcodingThreads: '' | 52 | transcodingThreads: '' |
44 | } | 53 | } |
45 | validationMessages = { | 54 | validationMessages = { |
55 | instanceName: INSTANCE_NAME.MESSAGES, | ||
46 | cachePreviewsSize: CACHE_PREVIEWS_SIZE.MESSAGES, | 56 | cachePreviewsSize: CACHE_PREVIEWS_SIZE.MESSAGES, |
47 | signupLimit: SIGNUP_LIMIT.MESSAGES, | 57 | signupLimit: SIGNUP_LIMIT.MESSAGES, |
48 | adminEmail: ADMIN_EMAIL.MESSAGES, | 58 | adminEmail: ADMIN_EMAIL.MESSAGES, |
@@ -65,6 +75,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
65 | 75 | ||
66 | buildForm () { | 76 | buildForm () { |
67 | const formGroupData = { | 77 | const formGroupData = { |
78 | instanceName: [ '', INSTANCE_NAME.VALIDATORS ], | ||
79 | instanceDescription: [ '' ], | ||
80 | instanceTerms: [ '' ], | ||
68 | cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ], | 81 | cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ], |
69 | signupEnabled: [ ], | 82 | signupEnabled: [ ], |
70 | signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ], | 83 | signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ], |
@@ -109,6 +122,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
109 | 122 | ||
110 | formValidated () { | 123 | formValidated () { |
111 | const data = { | 124 | const data = { |
125 | instance: { | ||
126 | name: this.form.value['instanceName'], | ||
127 | description: this.form.value['instanceDescription'], | ||
128 | terms: this.form.value['instanceTerms'] | ||
129 | }, | ||
112 | cache: { | 130 | cache: { |
113 | previews: { | 131 | previews: { |
114 | size: this.form.value['cachePreviewsSize'] | 132 | size: this.form.value['cachePreviewsSize'] |
@@ -146,6 +164,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
146 | this.serverService.loadConfig() | 164 | this.serverService.loadConfig() |
147 | 165 | ||
148 | this.updateForm() | 166 | this.updateForm() |
167 | |||
168 | this.notificationsService.success('Success', 'Configuration updated.') | ||
149 | }, | 169 | }, |
150 | 170 | ||
151 | err => this.notificationsService.error('Error', err.message) | 171 | err => this.notificationsService.error('Error', err.message) |
@@ -154,6 +174,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
154 | 174 | ||
155 | private updateForm () { | 175 | private updateForm () { |
156 | const data = { | 176 | const data = { |
177 | instanceName: this.customConfig.instance.name, | ||
178 | instanceDescription: this.customConfig.instance.description, | ||
179 | instanceTerms: this.customConfig.instance.terms, | ||
157 | cachePreviewsSize: this.customConfig.cache.previews.size, | 180 | cachePreviewsSize: this.customConfig.cache.previews.size, |
158 | signupEnabled: this.customConfig.signup.enabled, | 181 | signupEnabled: this.customConfig.signup.enabled, |
159 | signupLimit: this.customConfig.signup.limit, | 182 | signupLimit: this.customConfig.signup.limit, |
diff --git a/client/src/app/shared/forms/form-validators/custom-config.ts b/client/src/app/shared/forms/form-validators/custom-config.ts index 17ae0e75c..9e3fa98d8 100644 --- a/client/src/app/shared/forms/form-validators/custom-config.ts +++ b/client/src/app/shared/forms/form-validators/custom-config.ts | |||
@@ -1,5 +1,12 @@ | |||
1 | import { Validators } from '@angular/forms' | 1 | import { Validators } from '@angular/forms' |
2 | 2 | ||
3 | export const INSTANCE_NAME = { | ||
4 | VALIDATORS: [ Validators.required ], | ||
5 | MESSAGES: { | ||
6 | 'required': 'Instance name is required.', | ||
7 | } | ||
8 | } | ||
9 | |||
3 | export const CACHE_PREVIEWS_SIZE = { | 10 | export const CACHE_PREVIEWS_SIZE = { |
4 | VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ], | 11 | VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ], |
5 | MESSAGES: { | 12 | MESSAGES: { |
diff --git a/client/src/app/shared/forms/markdown-textarea.component.html b/client/src/app/shared/forms/markdown-textarea.component.html new file mode 100644 index 000000000..d2d4cf95c --- /dev/null +++ b/client/src/app/shared/forms/markdown-textarea.component.html | |||
@@ -0,0 +1,12 @@ | |||
1 | <div class="root" [ngStyle]="{ 'flex-direction': flexDirection }"> | ||
2 | <textarea | ||
3 | [(ngModel)]="description" (ngModelChange)="onModelChange()" | ||
4 | [ngClass]="classes" [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }" | ||
5 | id="description" name="description"> | ||
6 | </textarea> | ||
7 | |||
8 | <tabset *ngIf="arePreviewsDisplayed()" #staticTabs class="previews"> | ||
9 | <tab *ngIf="truncate !== undefined" heading="Truncated description preview" [innerHTML]="truncatedDescriptionHTML"></tab> | ||
10 | <tab heading="Complete description preview" [innerHTML]="descriptionHTML"></tab> | ||
11 | </tabset> | ||
12 | </div> | ||
diff --git a/client/src/app/shared/forms/markdown-textarea.component.scss b/client/src/app/shared/forms/markdown-textarea.component.scss new file mode 100644 index 000000000..82aff541d --- /dev/null +++ b/client/src/app/shared/forms/markdown-textarea.component.scss | |||
@@ -0,0 +1,27 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .root { | ||
5 | display: flex; | ||
6 | |||
7 | textarea { | ||
8 | @include peertube-textarea(100%, 150px); | ||
9 | |||
10 | margin-bottom: 15px; | ||
11 | } | ||
12 | |||
13 | /deep/ { | ||
14 | .nav-link { | ||
15 | display: flex !important; | ||
16 | align-items: center; | ||
17 | height: 30px !important; | ||
18 | padding: 0 15px !important; | ||
19 | } | ||
20 | |||
21 | .tab-content { | ||
22 | min-height: 75px; | ||
23 | padding: 15px; | ||
24 | font-size: 15px; | ||
25 | } | ||
26 | } | ||
27 | } | ||
diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.ts b/client/src/app/shared/forms/markdown-textarea.component.ts index eba345412..20f13b28c 100644 --- a/client/src/app/videos/+video-edit/shared/video-description.component.ts +++ b/client/src/app/shared/forms/markdown-textarea.component.ts | |||
@@ -3,25 +3,33 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | |||
3 | import 'rxjs/add/operator/debounceTime' | 3 | import 'rxjs/add/operator/debounceTime' |
4 | import 'rxjs/add/operator/distinctUntilChanged' | 4 | import 'rxjs/add/operator/distinctUntilChanged' |
5 | import { isInMobileView } from '@app/shared/misc/utils' | 5 | import { isInMobileView } from '@app/shared/misc/utils' |
6 | import { MarkdownService } from '@app/videos/shared' | ||
6 | import { Subject } from 'rxjs/Subject' | 7 | import { Subject } from 'rxjs/Subject' |
7 | import { MarkdownService } from '../../shared' | ||
8 | import truncate from 'lodash-es/truncate' | 8 | import truncate from 'lodash-es/truncate' |
9 | 9 | ||
10 | @Component({ | 10 | @Component({ |
11 | selector: 'my-video-description', | 11 | selector: 'my-markdown-textarea', |
12 | templateUrl: './video-description.component.html', | 12 | templateUrl: './markdown-textarea.component.html', |
13 | styleUrls: [ './video-description.component.scss' ], | 13 | styleUrls: [ './markdown-textarea.component.scss' ], |
14 | providers: [ | 14 | providers: [ |
15 | { | 15 | { |
16 | provide: NG_VALUE_ACCESSOR, | 16 | provide: NG_VALUE_ACCESSOR, |
17 | useExisting: forwardRef(() => VideoDescriptionComponent), | 17 | useExisting: forwardRef(() => MarkdownTextareaComponent), |
18 | multi: true | 18 | multi: true |
19 | } | 19 | } |
20 | ] | 20 | ] |
21 | }) | 21 | }) |
22 | 22 | ||
23 | export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { | 23 | export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { |
24 | @Input() description = '' | 24 | @Input() description = '' |
25 | @Input() classes: string[] = [] | ||
26 | @Input() textareaWidth = '100%' | ||
27 | @Input() textareaHeight = '150px' | ||
28 | @Input() previewColumn = false | ||
29 | @Input() truncate: number | ||
30 | |||
31 | textareaMarginRight = '0' | ||
32 | flexDirection = 'column' | ||
25 | truncatedDescriptionHTML = '' | 33 | truncatedDescriptionHTML = '' |
26 | descriptionHTML = '' | 34 | descriptionHTML = '' |
27 | 35 | ||
@@ -36,6 +44,11 @@ export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { | |||
36 | .subscribe(() => this.updateDescriptionPreviews()) | 44 | .subscribe(() => this.updateDescriptionPreviews()) |
37 | 45 | ||
38 | this.descriptionChanged.next(this.description) | 46 | this.descriptionChanged.next(this.description) |
47 | |||
48 | if (this.previewColumn) { | ||
49 | this.flexDirection = 'row' | ||
50 | this.textareaMarginRight = '15px' | ||
51 | } | ||
39 | } | 52 | } |
40 | 53 | ||
41 | propagateChange = (_: any) => { /* empty */ } | 54 | propagateChange = (_: any) => { /* empty */ } |
@@ -65,9 +78,9 @@ export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { | |||
65 | } | 78 | } |
66 | 79 | ||
67 | private updateDescriptionPreviews () { | 80 | private updateDescriptionPreviews () { |
68 | if (!this.description) return | 81 | if (this.description === null || this.description === undefined) return |
69 | 82 | ||
70 | this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 })) | 83 | this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: this.truncate })) |
71 | this.descriptionHTML = this.markdownService.markdownToHTML(this.description) | 84 | this.descriptionHTML = this.markdownService.markdownToHTML(this.description) |
72 | } | 85 | } |
73 | } | 86 | } |
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index a5c56cb46..d8f98bdf6 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts | |||
@@ -3,10 +3,13 @@ import { HttpClientModule } from '@angular/common/http' | |||
3 | import { NgModule } from '@angular/core' | 3 | import { NgModule } from '@angular/core' |
4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' | 4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' |
5 | import { RouterModule } from '@angular/router' | 5 | import { RouterModule } from '@angular/router' |
6 | import { MarkdownTextareaComponent } from '@app/shared/forms/markdown-textarea.component' | ||
7 | import { MarkdownService } from '@app/videos/shared' | ||
6 | import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' | 8 | import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' |
7 | 9 | ||
8 | import { BsDropdownModule } from 'ngx-bootstrap/dropdown' | 10 | import { BsDropdownModule } from 'ngx-bootstrap/dropdown' |
9 | import { ModalModule } from 'ngx-bootstrap/modal' | 11 | import { ModalModule } from 'ngx-bootstrap/modal' |
12 | import { TabsModule } from 'ngx-bootstrap/tabs' | ||
10 | import { InfiniteScrollModule } from 'ngx-infinite-scroll' | 13 | import { InfiniteScrollModule } from 'ngx-infinite-scroll' |
11 | import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' | 14 | import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' |
12 | import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' | 15 | import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' |
@@ -40,7 +43,8 @@ import { VideoService } from './video/video.service' | |||
40 | 43 | ||
41 | PrimeSharedModule, | 44 | PrimeSharedModule, |
42 | InfiniteScrollModule, | 45 | InfiniteScrollModule, |
43 | NgPipesModule | 46 | NgPipesModule, |
47 | TabsModule.forRoot() | ||
44 | ], | 48 | ], |
45 | 49 | ||
46 | declarations: [ | 50 | declarations: [ |
@@ -50,7 +54,8 @@ import { VideoService } from './video/video.service' | |||
50 | DeleteButtonComponent, | 54 | DeleteButtonComponent, |
51 | EditButtonComponent, | 55 | EditButtonComponent, |
52 | NumberFormatterPipe, | 56 | NumberFormatterPipe, |
53 | FromNowPipe | 57 | FromNowPipe, |
58 | MarkdownTextareaComponent | ||
54 | ], | 59 | ], |
55 | 60 | ||
56 | exports: [ | 61 | exports: [ |
@@ -74,6 +79,7 @@ import { VideoService } from './video/video.service' | |||
74 | VideoMiniatureComponent, | 79 | VideoMiniatureComponent, |
75 | DeleteButtonComponent, | 80 | DeleteButtonComponent, |
76 | EditButtonComponent, | 81 | EditButtonComponent, |
82 | MarkdownTextareaComponent, | ||
77 | 83 | ||
78 | NumberFormatterPipe, | 84 | NumberFormatterPipe, |
79 | FromNowPipe | 85 | FromNowPipe |
@@ -86,7 +92,8 @@ import { VideoService } from './video/video.service' | |||
86 | VideoAbuseService, | 92 | VideoAbuseService, |
87 | VideoBlacklistService, | 93 | VideoBlacklistService, |
88 | UserService, | 94 | UserService, |
89 | VideoService | 95 | VideoService, |
96 | MarkdownService | ||
90 | ] | 97 | ] |
91 | }) | 98 | }) |
92 | export class SharedModule { } | 99 | export class SharedModule { } |
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index 50761ca0c..d4f5e258f 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts | |||
@@ -42,10 +42,10 @@ export class VideoService { | |||
42 | } | 42 | } |
43 | 43 | ||
44 | updateVideo (video: VideoEdit) { | 44 | updateVideo (video: VideoEdit) { |
45 | const language = video.language || undefined | 45 | const language = video.language || null |
46 | const licence = video.licence || undefined | 46 | const licence = video.licence || null |
47 | const category = video.category || undefined | 47 | const category = video.category || null |
48 | const description = video.description || undefined | 48 | const description = video.description || null |
49 | 49 | ||
50 | const body: VideoUpdate = { | 50 | const body: VideoUpdate = { |
51 | name: video.name, | 51 | name: video.name, |
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 deleted file mode 100644 index 989292c47..000000000 --- a/client/src/app/videos/+video-edit/shared/video-description.component.html +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | <textarea | ||
2 | [(ngModel)]="description" (ngModelChange)="onModelChange()" | ||
3 | id="description" name="description"> | ||
4 | </textarea> | ||
5 | |||
6 | <tabset *ngIf="arePreviewsDisplayed()" #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 deleted file mode 100644 index 2c731bee3..000000000 --- a/client/src/app/videos/+video-edit/shared/video-description.component.scss +++ /dev/null | |||
@@ -1,24 +0,0 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | textarea { | ||
5 | @include peertube-textarea(100%, 150px); | ||
6 | |||
7 | margin-bottom: 15px; | ||
8 | } | ||
9 | |||
10 | /deep/ { | ||
11 | .nav-link { | ||
12 | display: flex !important; | ||
13 | align-items: center; | ||
14 | height: 30px !important; | ||
15 | padding: 0 15px !important; | ||
16 | } | ||
17 | |||
18 | .tab-content { | ||
19 | min-height: 75px; | ||
20 | padding: 15px; | ||
21 | font-size: 15px; | ||
22 | } | ||
23 | } | ||
24 | |||
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 80377933e..d031825bd 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 | |||
@@ -12,14 +12,14 @@ | |||
12 | <div class="form-group"> | 12 | <div class="form-group"> |
13 | <label class="label-tags">Tags</label> <span>(press Enter to add)</span> | 13 | <label class="label-tags">Tags</label> <span>(press Enter to add)</span> |
14 | <tag-input | 14 | <tag-input |
15 | [ngModel]="tags" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages" | 15 | [ngModel]="tags" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages" |
16 | formControlName="tags" maxItems="5" modelAsStrings="true" | 16 | formControlName="tags" maxItems="5" modelAsStrings="true" |
17 | ></tag-input> | 17 | ></tag-input> |
18 | </div> | 18 | </div> |
19 | 19 | ||
20 | <div class="form-group"> | 20 | <div class="form-group"> |
21 | <label for="description">Description</label> | 21 | <label for="description">Description</label> |
22 | <my-video-description formControlName="description"></my-video-description> | 22 | <my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea> |
23 | 23 | ||
24 | <div *ngIf="formErrors.description" class="form-error"> | 24 | <div *ngIf="formErrors.description" class="form-error"> |
25 | {{ formErrors.description }} | 25 | {{ formErrors.description }} |
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 ce106d82f..098a71ae6 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 | |||
@@ -1,23 +1,17 @@ | |||
1 | import { NgModule } from '@angular/core' | 1 | import { NgModule } from '@angular/core' |
2 | |||
3 | import { TagInputModule } from 'ngx-chips' | ||
4 | import { TabsModule } from 'ngx-bootstrap/tabs' | 2 | import { TabsModule } from 'ngx-bootstrap/tabs' |
5 | 3 | import { TagInputModule } from 'ngx-chips' | |
6 | import { MarkdownService } from '../../shared' | ||
7 | import { SharedModule } from '../../../shared' | 4 | import { SharedModule } from '../../../shared' |
8 | import { VideoDescriptionComponent } from './video-description.component' | ||
9 | import { VideoEditComponent } from './video-edit.component' | 5 | import { VideoEditComponent } from './video-edit.component' |
10 | 6 | ||
11 | @NgModule({ | 7 | @NgModule({ |
12 | imports: [ | 8 | imports: [ |
13 | TagInputModule, | 9 | TagInputModule, |
14 | TabsModule.forRoot(), | ||
15 | 10 | ||
16 | SharedModule | 11 | SharedModule |
17 | ], | 12 | ], |
18 | 13 | ||
19 | declarations: [ | 14 | declarations: [ |
20 | VideoDescriptionComponent, | ||
21 | VideoEditComponent | 15 | VideoEditComponent |
22 | ], | 16 | ], |
23 | 17 | ||
@@ -25,12 +19,9 @@ import { VideoEditComponent } from './video-edit.component' | |||
25 | TagInputModule, | 19 | TagInputModule, |
26 | TabsModule, | 20 | TabsModule, |
27 | 21 | ||
28 | VideoDescriptionComponent, | ||
29 | VideoEditComponent | 22 | VideoEditComponent |
30 | ], | 23 | ], |
31 | 24 | ||
32 | providers: [ | 25 | providers: [] |
33 | MarkdownService | ||
34 | ] | ||
35 | }) | 26 | }) |
36 | export class VideoEditModule { } | 27 | export class VideoEditModule { } |
diff --git a/client/src/app/videos/shared/markdown.service.ts b/client/src/app/videos/shared/markdown.service.ts index 82745f0c6..fd0330f9b 100644 --- a/client/src/app/videos/shared/markdown.service.ts +++ b/client/src/app/videos/shared/markdown.service.ts | |||
@@ -14,6 +14,17 @@ export class MarkdownService { | |||
14 | .enable('link') | 14 | .enable('link') |
15 | .enable('newline') | 15 | .enable('newline') |
16 | 16 | ||
17 | this.setTargetToLinks() | ||
18 | } | ||
19 | |||
20 | markdownToHTML (markdown: string) { | ||
21 | const html = this.markdownIt.render(markdown) | ||
22 | |||
23 | // Avoid linkify truncated links | ||
24 | return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...') | ||
25 | } | ||
26 | |||
27 | private setTargetToLinks () { | ||
17 | // Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer | 28 | // Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer |
18 | const defaultRender = this.markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) { | 29 | const defaultRender = this.markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) { |
19 | return self.renderToken(tokens, idx, options) | 30 | return self.renderToken(tokens, idx, options) |
@@ -33,11 +44,4 @@ export class MarkdownService { | |||
33 | return defaultRender(tokens, idx, options, env, self) | 44 | return defaultRender(tokens, idx, options, env, self) |
34 | } | 45 | } |
35 | } | 46 | } |
36 | |||
37 | markdownToHTML (markdown: string) { | ||
38 | const html = this.markdownIt.render(markdown) | ||
39 | |||
40 | // Avoid linkify truncated links | ||
41 | return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...') | ||
42 | } | ||
43 | } | 47 | } |