diff options
Diffstat (limited to 'client')
21 files changed, 126 insertions, 79 deletions
diff --git a/client/package.json b/client/package.json index 92659b091..08d309b07 100644 --- a/client/package.json +++ b/client/package.json | |||
@@ -93,6 +93,7 @@ | |||
93 | "@types/jasminewd2": "^2.0.3", | 93 | "@types/jasminewd2": "^2.0.3", |
94 | "@types/jest": "^23.3.1", | 94 | "@types/jest": "^23.3.1", |
95 | "@types/jschannel": "^1.0.0", | 95 | "@types/jschannel": "^1.0.0", |
96 | "@types/linkifyjs": "^2.1.1", | ||
96 | "@types/lodash-es": "^4.17.0", | 97 | "@types/lodash-es": "^4.17.0", |
97 | "@types/markdown-it": "^0.0.5", | 98 | "@types/markdown-it": "^0.0.5", |
98 | "@types/node": "^10.9.2", | 99 | "@types/node": "^10.9.2", |
@@ -154,7 +155,7 @@ | |||
154 | "ts-jest": "^23.1.4", | 155 | "ts-jest": "^23.1.4", |
155 | "tslint": "^5.7.0", | 156 | "tslint": "^5.7.0", |
156 | "tslint-config-standard": "^8.0.1", | 157 | "tslint-config-standard": "^8.0.1", |
157 | "typescript": "3.1.6", | 158 | "typescript": "3.2", |
158 | "video.js": "^7", | 159 | "video.js": "^7", |
159 | "videojs-contextmenu-ui": "^5.0.0", | 160 | "videojs-contextmenu-ui": "^5.0.0", |
160 | "videojs-contrib-quality-levels": "^2.0.9", | 161 | "videojs-contrib-quality-levels": "^2.0.9", |
diff --git a/client/src/app/+about/about-instance/about-instance.component.ts b/client/src/app/+about/about-instance/about-instance.component.ts index a1b30fa8c..ec572ff80 100644 --- a/client/src/app/+about/about-instance/about-instance.component.ts +++ b/client/src/app/+about/about-instance/about-instance.component.ts | |||
@@ -44,10 +44,11 @@ export class AboutInstanceComponent implements OnInit { | |||
44 | ngOnInit () { | 44 | ngOnInit () { |
45 | this.instanceService.getAbout() | 45 | this.instanceService.getAbout() |
46 | .subscribe( | 46 | .subscribe( |
47 | res => { | 47 | async res => { |
48 | this.shortDescription = res.instance.shortDescription | 48 | this.shortDescription = res.instance.shortDescription |
49 | this.descriptionHTML = this.markdownService.textMarkdownToHTML(res.instance.description) | 49 | |
50 | this.termsHTML = this.markdownService.textMarkdownToHTML(res.instance.terms) | 50 | this.descriptionHTML = await this.markdownService.textMarkdownToHTML(res.instance.description) |
51 | this.termsHTML = await this.markdownService.textMarkdownToHTML(res.instance.terms) | ||
51 | }, | 52 | }, |
52 | 53 | ||
53 | () => this.notifier.error(this.i18n('Cannot get about information from server')) | 54 | () => this.notifier.error(this.i18n('Cannot get about information from server')) |
diff --git a/client/src/app/+accounts/account-about/account-about.component.ts b/client/src/app/+accounts/account-about/account-about.component.ts index 13890a0ee..ce22d3c2e 100644 --- a/client/src/app/+accounts/account-about/account-about.component.ts +++ b/client/src/app/+accounts/account-about/account-about.component.ts | |||
@@ -25,9 +25,9 @@ export class AccountAboutComponent implements OnInit, OnDestroy { | |||
25 | ngOnInit () { | 25 | ngOnInit () { |
26 | // Parent get the account for us | 26 | // Parent get the account for us |
27 | this.accountSub = this.accountService.accountLoaded | 27 | this.accountSub = this.accountService.accountLoaded |
28 | .subscribe(account => { | 28 | .subscribe(async account => { |
29 | this.account = account | 29 | this.account = account |
30 | this.descriptionHTML = this.markdownService.textMarkdownToHTML(this.account.description) | 30 | this.descriptionHTML = await this.markdownService.textMarkdownToHTML(this.account.description) |
31 | }) | 31 | }) |
32 | } | 32 | } |
33 | 33 | ||
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html index 05b549de6..627437053 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html +++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html | |||
@@ -51,11 +51,11 @@ | |||
51 | <td class="moderation-expanded" colspan="6"> | 51 | <td class="moderation-expanded" colspan="6"> |
52 | <div> | 52 | <div> |
53 | <span i18n class="moderation-expanded-label">Reason:</span> | 53 | <span i18n class="moderation-expanded-label">Reason:</span> |
54 | <span class="moderation-expanded-text" [innerHTML]="toHtml(videoAbuse.reason)"></span> | 54 | <span class="moderation-expanded-text" [innerHTML]="videoAbuse.reasonHtml"></span> |
55 | </div> | 55 | </div> |
56 | <div *ngIf="videoAbuse.moderationComment"> | 56 | <div *ngIf="videoAbuse.moderationComment"> |
57 | <span i18n class="moderation-expanded-label">Moderation comment:</span> | 57 | <span i18n class="moderation-expanded-label">Moderation comment:</span> |
58 | <span class="moderation-expanded-text" [innerHTML]="toHtml(videoAbuse.moderationComment)"></span> | 58 | <span class="moderation-expanded-text" [innerHTML]="videoAbuse.moderationCommentHtml"></span> |
59 | </div> | 59 | </div> |
60 | </td> | 60 | </td> |
61 | </tr> | 61 | </tr> |
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts index 00c871659..3aa875668 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts +++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts | |||
@@ -19,7 +19,7 @@ import { MarkdownService } from '@app/shared/renderer' | |||
19 | export class VideoAbuseListComponent extends RestTable implements OnInit { | 19 | export class VideoAbuseListComponent extends RestTable implements OnInit { |
20 | @ViewChild('moderationCommentModal') moderationCommentModal: ModerationCommentModalComponent | 20 | @ViewChild('moderationCommentModal') moderationCommentModal: ModerationCommentModalComponent |
21 | 21 | ||
22 | videoAbuses: VideoAbuse[] = [] | 22 | videoAbuses: (VideoAbuse & { moderationCommentHtml?: string, reasonHtml?: string })[] = [] |
23 | totalRecords = 0 | 23 | totalRecords = 0 |
24 | rowsPerPage = 10 | 24 | rowsPerPage = 10 |
25 | sort: SortMeta = { field: 'createdAt', order: 1 } | 25 | sort: SortMeta = { field: 'createdAt', order: 1 } |
@@ -110,19 +110,28 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { | |||
110 | 110 | ||
111 | } | 111 | } |
112 | 112 | ||
113 | toHtml (text: string) { | ||
114 | return this.markdownRenderer.textMarkdownToHTML(text) | ||
115 | } | ||
116 | |||
117 | protected loadData () { | 113 | protected loadData () { |
118 | return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort) | 114 | return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort) |
119 | .subscribe( | 115 | .subscribe( |
120 | resultList => { | 116 | async resultList => { |
121 | this.videoAbuses = resultList.data | ||
122 | this.totalRecords = resultList.total | 117 | this.totalRecords = resultList.total |
118 | |||
119 | this.videoAbuses = resultList.data | ||
120 | |||
121 | for (const abuse of this.videoAbuses) { | ||
122 | Object.assign(abuse, { | ||
123 | reasonHtml: await this.toHtml(abuse.reason), | ||
124 | moderationCommentHtml: await this.toHtml(abuse.moderationComment) | ||
125 | }) | ||
126 | } | ||
127 | |||
123 | }, | 128 | }, |
124 | 129 | ||
125 | err => this.notifier.error(err.message) | 130 | err => this.notifier.error(err.message) |
126 | ) | 131 | ) |
127 | } | 132 | } |
133 | |||
134 | private toHtml (text: string) { | ||
135 | return this.markdownRenderer.textMarkdownToHTML(text) | ||
136 | } | ||
128 | } | 137 | } |
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html index 247f441c1..608dff2d8 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html | |||
@@ -41,7 +41,7 @@ | |||
41 | <tr> | 41 | <tr> |
42 | <td class="moderation-expanded" colspan="6"> | 42 | <td class="moderation-expanded" colspan="6"> |
43 | <span i18n class="moderation-expanded-label">Blacklist reason:</span> | 43 | <span i18n class="moderation-expanded-label">Blacklist reason:</span> |
44 | <span class="moderation-expanded-text" [innerHTML]="toHtml(videoBlacklist.reason)"></span> | 44 | <span class="moderation-expanded-text" [innerHTML]="videoBlacklist.reasonHtml"></span> |
45 | </td> | 45 | </td> |
46 | </tr> | 46 | </tr> |
47 | </ng-template> | 47 | </ng-template> |
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts index b27bbbfef..5443d816d 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts | |||
@@ -15,7 +15,7 @@ import { MarkdownService } from '@app/shared/renderer' | |||
15 | styleUrls: [ '../moderation.component.scss' ] | 15 | styleUrls: [ '../moderation.component.scss' ] |
16 | }) | 16 | }) |
17 | export class VideoBlacklistListComponent extends RestTable implements OnInit { | 17 | export class VideoBlacklistListComponent extends RestTable implements OnInit { |
18 | blacklist: VideoBlacklist[] = [] | 18 | blacklist: (VideoBlacklist & { reasonHtml?: string })[] = [] |
19 | totalRecords = 0 | 19 | totalRecords = 0 |
20 | rowsPerPage = 10 | 20 | rowsPerPage = 10 |
21 | sort: SortMeta = { field: 'createdAt', order: 1 } | 21 | sort: SortMeta = { field: 'createdAt', order: 1 } |
@@ -79,9 +79,14 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { | |||
79 | protected loadData () { | 79 | protected loadData () { |
80 | this.videoBlacklistService.listBlacklist(this.pagination, this.sort) | 80 | this.videoBlacklistService.listBlacklist(this.pagination, this.sort) |
81 | .subscribe( | 81 | .subscribe( |
82 | resultList => { | 82 | async resultList => { |
83 | this.blacklist = resultList.data | ||
84 | this.totalRecords = resultList.total | 83 | this.totalRecords = resultList.total |
84 | |||
85 | this.blacklist = resultList.data | ||
86 | |||
87 | for (const element of this.blacklist) { | ||
88 | Object.assign(element, { reasonHtml: await this.toHtml(element.reason) }) | ||
89 | } | ||
85 | }, | 90 | }, |
86 | 91 | ||
87 | err => this.notifier.error(err.message) | 92 | err => this.notifier.error(err.message) |
diff --git a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts b/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts index 895b19064..11f9391e1 100644 --- a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts +++ b/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts | |||
@@ -26,11 +26,11 @@ export class VideoChannelAboutComponent implements OnInit, OnDestroy { | |||
26 | ngOnInit () { | 26 | ngOnInit () { |
27 | // Parent get the video channel for us | 27 | // Parent get the video channel for us |
28 | this.videoChannelSub = this.videoChannelService.videoChannelLoaded | 28 | this.videoChannelSub = this.videoChannelService.videoChannelLoaded |
29 | .subscribe(videoChannel => { | 29 | .subscribe(async videoChannel => { |
30 | this.videoChannel = videoChannel | 30 | this.videoChannel = videoChannel |
31 | 31 | ||
32 | this.descriptionHTML = this.markdownService.textMarkdownToHTML(this.videoChannel.description) | 32 | this.descriptionHTML = await this.markdownService.textMarkdownToHTML(this.videoChannel.description) |
33 | this.supportHTML = this.markdownService.enhancedMarkdownToHTML(this.videoChannel.support) | 33 | this.supportHTML = await this.markdownService.enhancedMarkdownToHTML(this.videoChannel.support) |
34 | }) | 34 | }) |
35 | } | 35 | } |
36 | 36 | ||
diff --git a/client/src/app/core/notification/user-notification-socket.service.ts b/client/src/app/core/notification/user-notification-socket.service.ts index f367d9ae4..29337d3a7 100644 --- a/client/src/app/core/notification/user-notification-socket.service.ts +++ b/client/src/app/core/notification/user-notification-socket.service.ts | |||
@@ -21,21 +21,22 @@ export class UserNotificationSocket { | |||
21 | this.notificationSubject.next({ type, notification }) | 21 | this.notificationSubject.next({ type, notification }) |
22 | } | 22 | } |
23 | 23 | ||
24 | getMyNotificationsSocket () { | 24 | async getMyNotificationsSocket () { |
25 | const socket = this.getSocket() | 25 | await this.initSocket() |
26 | |||
27 | socket.on('new-notification', (n: UserNotificationServer) => this.dispatch('new', n)) | ||
28 | 26 | ||
29 | return this.notificationSubject.asObservable() | 27 | return this.notificationSubject.asObservable() |
30 | } | 28 | } |
31 | 29 | ||
32 | private getSocket () { | 30 | private async initSocket () { |
33 | if (this.socket) return this.socket | 31 | if (this.socket) return |
32 | |||
33 | // FIXME: import('..') returns a struct module, containing a "default" field corresponding to our sanitizeHtml function | ||
34 | const io: typeof import ('socket.io-client') = (await import('socket.io-client') as any).default | ||
34 | 35 | ||
35 | this.socket = io(environment.apiUrl + '/user-notifications', { | 36 | this.socket = io(environment.apiUrl + '/user-notifications', { |
36 | query: { accessToken: this.auth.getAccessToken() } | 37 | query: { accessToken: this.auth.getAccessToken() } |
37 | }) | 38 | }) |
38 | 39 | ||
39 | return this.socket | 40 | this.socket.on('new-notification', (n: UserNotificationServer) => this.dispatch('new', n)) |
40 | } | 41 | } |
41 | } | 42 | } |
diff --git a/client/src/app/menu/avatar-notification.component.ts b/client/src/app/menu/avatar-notification.component.ts index f1af08096..878c5c88c 100644 --- a/client/src/app/menu/avatar-notification.component.ts +++ b/client/src/app/menu/avatar-notification.component.ts | |||
@@ -26,18 +26,19 @@ export class AvatarNotificationComponent implements OnInit, OnDestroy { | |||
26 | private userNotificationSocket: UserNotificationSocket, | 26 | private userNotificationSocket: UserNotificationSocket, |
27 | private notifier: Notifier, | 27 | private notifier: Notifier, |
28 | private router: Router | 28 | private router: Router |
29 | ) {} | 29 | ) { |
30 | } | ||
30 | 31 | ||
31 | ngOnInit () { | 32 | ngOnInit () { |
32 | this.userNotificationService.countUnreadNotifications() | 33 | this.userNotificationService.countUnreadNotifications() |
33 | .subscribe( | 34 | .subscribe( |
34 | result => { | 35 | result => { |
35 | this.unreadNotifications = Math.min(result, 99) // Limit number to 99 | 36 | this.unreadNotifications = Math.min(result, 99) // Limit number to 99 |
36 | this.subscribeToNotifications() | 37 | this.subscribeToNotifications() |
37 | }, | 38 | }, |
38 | 39 | ||
39 | err => this.notifier.error(err.message) | 40 | err => this.notifier.error(err.message) |
40 | ) | 41 | ) |
41 | 42 | ||
42 | this.routeSub = this.router.events | 43 | this.routeSub = this.router.events |
43 | .pipe(filter(event => event instanceof NavigationEnd)) | 44 | .pipe(filter(event => event instanceof NavigationEnd)) |
@@ -53,13 +54,14 @@ export class AvatarNotificationComponent implements OnInit, OnDestroy { | |||
53 | this.popover.close() | 54 | this.popover.close() |
54 | } | 55 | } |
55 | 56 | ||
56 | private subscribeToNotifications () { | 57 | private async subscribeToNotifications () { |
57 | this.notificationSub = this.userNotificationSocket.getMyNotificationsSocket() | 58 | const obs = await this.userNotificationSocket.getMyNotificationsSocket() |
58 | .subscribe(data => { | 59 | |
59 | if (data.type === 'new') return this.unreadNotifications++ | 60 | this.notificationSub = obs.subscribe(data => { |
60 | if (data.type === 'read') return this.unreadNotifications-- | 61 | if (data.type === 'new') return this.unreadNotifications++ |
61 | if (data.type === 'read-all') return this.unreadNotifications = 0 | 62 | if (data.type === 'read') return this.unreadNotifications-- |
62 | }) | 63 | if (data.type === 'read-all') return this.unreadNotifications = 0 |
64 | }) | ||
63 | } | 65 | } |
64 | 66 | ||
65 | } | 67 | } |
diff --git a/client/src/app/shared/forms/markdown-textarea.component.ts b/client/src/app/shared/forms/markdown-textarea.component.ts index e87aca0d4..49a57f29d 100644 --- a/client/src/app/shared/forms/markdown-textarea.component.ts +++ b/client/src/app/shared/forms/markdown-textarea.component.ts | |||
@@ -82,11 +82,11 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { | |||
82 | return this.screenService.isInSmallView() === false | 82 | return this.screenService.isInSmallView() === false |
83 | } | 83 | } |
84 | 84 | ||
85 | private updatePreviews () { | 85 | private async updatePreviews () { |
86 | if (this.content === null || this.content === undefined) return | 86 | if (this.content === null || this.content === undefined) return |
87 | 87 | ||
88 | this.truncatedPreviewHTML = this.markdownRender(truncate(this.content, { length: this.truncate })) | 88 | this.truncatedPreviewHTML = await this.markdownRender(truncate(this.content, { length: this.truncate })) |
89 | this.previewHTML = this.markdownRender(this.content) | 89 | this.previewHTML = await this.markdownRender(this.content) |
90 | } | 90 | } |
91 | 91 | ||
92 | private markdownRender (text: string) { | 92 | private markdownRender (text: string) { |
diff --git a/client/src/app/shared/renderer/html-renderer.service.ts b/client/src/app/shared/renderer/html-renderer.service.ts index d49df9b6d..28ef51e72 100644 --- a/client/src/app/shared/renderer/html-renderer.service.ts +++ b/client/src/app/shared/renderer/html-renderer.service.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | import { LinkifierService } from '@app/shared/renderer/linkifier.service' | 2 | import { LinkifierService } from '@app/shared/renderer/linkifier.service' |
3 | import * as sanitizeHtml from 'sanitize-html' | ||
4 | 3 | ||
5 | @Injectable() | 4 | @Injectable() |
6 | export class HtmlRendererService { | 5 | export class HtmlRendererService { |
@@ -9,7 +8,10 @@ export class HtmlRendererService { | |||
9 | 8 | ||
10 | } | 9 | } |
11 | 10 | ||
12 | toSafeHtml (text: string) { | 11 | async toSafeHtml (text: string) { |
12 | // FIXME: import('..') returns a struct module, containing a "default" field corresponding to our sanitizeHtml function | ||
13 | const sanitizeHtml: typeof import ('sanitize-html') = (await import('sanitize-html') as any).default | ||
14 | |||
13 | // Convert possible markdown to html | 15 | // Convert possible markdown to html |
14 | const html = this.linkifier.linkify(text) | 16 | const html = this.linkifier.linkify(text) |
15 | 17 | ||
diff --git a/client/src/app/shared/renderer/linkifier.service.ts b/client/src/app/shared/renderer/linkifier.service.ts index 2529c9eaf..95d5f17cc 100644 --- a/client/src/app/shared/renderer/linkifier.service.ts +++ b/client/src/app/shared/renderer/linkifier.service.ts | |||
@@ -1,8 +1,7 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | import { getAbsoluteAPIUrl } from '@app/shared/misc/utils' | 2 | import { getAbsoluteAPIUrl } from '@app/shared/misc/utils' |
3 | // FIXME: use @types/linkify when https://github.com/DefinitelyTyped/DefinitelyTyped/pull/29682/files is merged? | 3 | import * as linkify from 'linkifyjs' |
4 | const linkify = require('linkifyjs') | 4 | import linkifyHtml from 'linkifyjs/html' |
5 | const linkifyHtml = require('linkifyjs/html') | ||
6 | 5 | ||
7 | @Injectable() | 6 | @Injectable() |
8 | export class LinkifierService { | 7 | export class LinkifierService { |
diff --git a/client/src/app/shared/renderer/markdown.service.ts b/client/src/app/shared/renderer/markdown.service.ts index 07017eca5..69dc60aaf 100644 --- a/client/src/app/shared/renderer/markdown.service.ts +++ b/client/src/app/shared/renderer/markdown.service.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | 2 | ||
3 | import * as MarkdownIt from 'markdown-it' | 3 | import { MarkdownIt } from 'markdown-it' |
4 | 4 | ||
5 | @Injectable() | 5 | @Injectable() |
6 | export class MarkdownService { | 6 | export class MarkdownService { |
@@ -14,30 +14,36 @@ export class MarkdownService { | |||
14 | ] | 14 | ] |
15 | static ENHANCED_RULES = MarkdownService.TEXT_RULES.concat([ 'image' ]) | 15 | static ENHANCED_RULES = MarkdownService.TEXT_RULES.concat([ 'image' ]) |
16 | 16 | ||
17 | private textMarkdownIt: MarkdownIt.MarkdownIt | 17 | private textMarkdownIt: MarkdownIt |
18 | private enhancedMarkdownIt: MarkdownIt.MarkdownIt | 18 | private enhancedMarkdownIt: MarkdownIt |
19 | 19 | ||
20 | constructor () { | 20 | async textMarkdownToHTML (markdown: string) { |
21 | this.textMarkdownIt = this.createMarkdownIt(MarkdownService.TEXT_RULES) | ||
22 | this.enhancedMarkdownIt = this.createMarkdownIt(MarkdownService.ENHANCED_RULES) | ||
23 | } | ||
24 | |||
25 | textMarkdownToHTML (markdown: string) { | ||
26 | if (!markdown) return '' | 21 | if (!markdown) return '' |
27 | 22 | ||
23 | if (!this.textMarkdownIt) { | ||
24 | this.textMarkdownIt = await this.createMarkdownIt(MarkdownService.TEXT_RULES) | ||
25 | } | ||
26 | |||
28 | const html = this.textMarkdownIt.render(markdown) | 27 | const html = this.textMarkdownIt.render(markdown) |
29 | return this.avoidTruncatedTags(html) | 28 | return this.avoidTruncatedTags(html) |
30 | } | 29 | } |
31 | 30 | ||
32 | enhancedMarkdownToHTML (markdown: string) { | 31 | async enhancedMarkdownToHTML (markdown: string) { |
33 | if (!markdown) return '' | 32 | if (!markdown) return '' |
34 | 33 | ||
34 | if (!this.enhancedMarkdownIt) { | ||
35 | this.enhancedMarkdownIt = await this.createMarkdownIt(MarkdownService.ENHANCED_RULES) | ||
36 | } | ||
37 | |||
35 | const html = this.enhancedMarkdownIt.render(markdown) | 38 | const html = this.enhancedMarkdownIt.render(markdown) |
36 | return this.avoidTruncatedTags(html) | 39 | return this.avoidTruncatedTags(html) |
37 | } | 40 | } |
38 | 41 | ||
39 | private createMarkdownIt (rules: string[]) { | 42 | private async createMarkdownIt (rules: string[]) { |
40 | const markdownIt = new MarkdownIt('zero', { linkify: true, breaks: true }) | 43 | // FIXME: import('..') returns a struct module, containing a "default" field corresponding to our sanitizeHtml function |
44 | const MarkdownItClass: typeof import ('markdown-it') = (await import('markdown-it') as any).default | ||
45 | |||
46 | const markdownIt = new MarkdownItClass('zero', { linkify: true, breaks: true }) | ||
41 | 47 | ||
42 | for (let rule of rules) { | 48 | for (let rule of rules) { |
43 | markdownIt.enable(rule) | 49 | markdownIt.enable(rule) |
@@ -48,7 +54,7 @@ export class MarkdownService { | |||
48 | return markdownIt | 54 | return markdownIt |
49 | } | 55 | } |
50 | 56 | ||
51 | private setTargetToLinks (markdownIt: MarkdownIt.MarkdownIt) { | 57 | private setTargetToLinks (markdownIt: MarkdownIt) { |
52 | // Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer | 58 | // Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer |
53 | const defaultRender = markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) { | 59 | const defaultRender = markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) { |
54 | return self.renderToken(tokens, idx, options) | 60 | return self.renderToken(tokens, idx, options) |
diff --git a/client/src/app/shared/users/user-notification.service.ts b/client/src/app/shared/users/user-notification.service.ts index f8a30955d..ae0bc9cb1 100644 --- a/client/src/app/shared/users/user-notification.service.ts +++ b/client/src/app/shared/users/user-notification.service.ts | |||
@@ -7,7 +7,7 @@ import { ResultList, UserNotification as UserNotificationServer, UserNotificatio | |||
7 | import { UserNotification } from './user-notification.model' | 7 | import { UserNotification } from './user-notification.model' |
8 | import { AuthService } from '../../core' | 8 | import { AuthService } from '../../core' |
9 | import { ComponentPagination } from '../rest/component-pagination.model' | 9 | import { ComponentPagination } from '../rest/component-pagination.model' |
10 | import { User } from '..' | 10 | import { User } from '../users/user.model' |
11 | import { UserNotificationSocket } from '@app/core/notification/user-notification-socket.service' | 11 | import { UserNotificationSocket } from '@app/core/notification/user-notification-socket.service' |
12 | 12 | ||
13 | @Injectable() | 13 | @Injectable() |
diff --git a/client/src/app/videos/+video-watch/comment/video-comment.component.ts b/client/src/app/videos/+video-watch/comment/video-comment.component.ts index aba7f9d1c..172eb0a39 100644 --- a/client/src/app/videos/+video-watch/comment/video-comment.component.ts +++ b/client/src/app/videos/+video-watch/comment/video-comment.component.ts | |||
@@ -85,8 +85,8 @@ export class VideoCommentComponent implements OnInit, OnChanges { | |||
85 | ) | 85 | ) |
86 | } | 86 | } |
87 | 87 | ||
88 | private init () { | 88 | private async init () { |
89 | this.sanitizedCommentHTML = this.htmlRenderer.toSafeHtml(this.comment.text) | 89 | this.sanitizedCommentHTML = await this.htmlRenderer.toSafeHtml(this.comment.text) |
90 | 90 | ||
91 | this.newParentComments = this.parentComments.concat([ this.comment ]) | 91 | this.newParentComments = this.parentComments.concat([ this.comment ]) |
92 | } | 92 | } |
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 index deb8fbc67..5e7afa012 100644 --- a/client/src/app/videos/+video-watch/modal/video-support.component.ts +++ b/client/src/app/videos/+video-watch/modal/video-support.component.ts | |||
@@ -21,7 +21,9 @@ export class VideoSupportComponent { | |||
21 | ) { } | 21 | ) { } |
22 | 22 | ||
23 | show () { | 23 | show () { |
24 | this.videoHTMLSupport = this.markdownService.enhancedMarkdownToHTML(this.video.support) | ||
25 | this.modalService.open(this.modal) | 24 | this.modalService.open(this.modal) |
25 | |||
26 | this.markdownService.enhancedMarkdownToHTML(this.video.support) | ||
27 | .then(r => this.videoHTMLSupport = r) | ||
26 | } | 28 | } |
27 | } | 29 | } |
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 4dbfa41e5..0f04441ba 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts | |||
@@ -325,8 +325,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
325 | this.setVideoDescriptionHTML() | 325 | this.setVideoDescriptionHTML() |
326 | } | 326 | } |
327 | 327 | ||
328 | private setVideoDescriptionHTML () { | 328 | private async setVideoDescriptionHTML () { |
329 | this.videoHTMLDescription = this.markdownService.textMarkdownToHTML(this.video.description) | 329 | this.videoHTMLDescription = await this.markdownService.textMarkdownToHTML(this.video.description) |
330 | } | 330 | } |
331 | 331 | ||
332 | private setVideoLikesBarTooltipText () { | 332 | private setVideoLikesBarTooltipText () { |
diff --git a/client/src/assets/player/peertube-plugin.ts b/client/src/assets/player/peertube-plugin.ts index 7ea4a06d4..92ac57cf5 100644 --- a/client/src/assets/player/peertube-plugin.ts +++ b/client/src/assets/player/peertube-plugin.ts | |||
@@ -22,7 +22,6 @@ import { | |||
22 | 22 | ||
23 | const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin') | 23 | const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin') |
24 | class PeerTubePlugin extends Plugin { | 24 | class PeerTubePlugin extends Plugin { |
25 | private readonly autoplay: boolean = false | ||
26 | private readonly startTime: number = 0 | 25 | private readonly startTime: number = 0 |
27 | private readonly videoViewUrl: string | 26 | private readonly videoViewUrl: string |
28 | private readonly videoDuration: number | 27 | private readonly videoDuration: number |
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss index e18e9ae9d..6fb9bf200 100644 --- a/client/src/sass/include/_mixins.scss +++ b/client/src/sass/include/_mixins.scss | |||
@@ -432,7 +432,7 @@ | |||
432 | height: 160px; | 432 | height: 160px; |
433 | display: flex; | 433 | display: flex; |
434 | flex-direction: column; | 434 | flex-direction: column; |
435 | align-items: start; | 435 | align-items: flex-start; |
436 | 436 | ||
437 | .actor { | 437 | .actor { |
438 | display: flex; | 438 | display: flex; |
diff --git a/client/yarn.lock b/client/yarn.lock index 63394e0f7..2d3ade3dd 100644 --- a/client/yarn.lock +++ b/client/yarn.lock | |||
@@ -445,6 +445,13 @@ | |||
445 | resolved "https://registry.yarnpkg.com/@types/jschannel/-/jschannel-1.0.1.tgz#79d582ccf42554c8457230526a3054d018d559f0" | 445 | resolved "https://registry.yarnpkg.com/@types/jschannel/-/jschannel-1.0.1.tgz#79d582ccf42554c8457230526a3054d018d559f0" |
446 | integrity sha512-S34NuOoOOKXbft3f9GDeLKp777ABCGArZaqUWOuu1Xn+1S75Osmk8kCeqmw5x2TuASyjE082DwDAuoaXNIRCTw== | 446 | integrity sha512-S34NuOoOOKXbft3f9GDeLKp777ABCGArZaqUWOuu1Xn+1S75Osmk8kCeqmw5x2TuASyjE082DwDAuoaXNIRCTw== |
447 | 447 | ||
448 | "@types/linkifyjs@^2.1.1": | ||
449 | version "2.1.1" | ||
450 | resolved "https://registry.yarnpkg.com/@types/linkifyjs/-/linkifyjs-2.1.1.tgz#d6902c165f7108ff9293f7145dfb703fee6814c7" | ||
451 | integrity sha512-rTXD/qsdI0aAf1tOtacWaE47K2QLz5C/g7rmB6kYyNuRKWMtStcQjVAM5R/T6kaiR8EVLMwPZ1RqX3aA/CS95g== | ||
452 | dependencies: | ||
453 | "@types/react" "*" | ||
454 | |||
448 | "@types/lodash-es@^4.17.0": | 455 | "@types/lodash-es@^4.17.0": |
449 | version "4.17.1" | 456 | version "4.17.1" |
450 | resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.1.tgz#56745e5411558362aeca31def918f88f725dd29d" | 457 | resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.1.tgz#56745e5411558362aeca31def918f88f725dd29d" |
@@ -500,11 +507,24 @@ | |||
500 | "@types/node" "*" | 507 | "@types/node" "*" |
501 | "@types/parse-torrent-file" "*" | 508 | "@types/parse-torrent-file" "*" |
502 | 509 | ||
510 | "@types/prop-types@*": | ||
511 | version "15.5.9" | ||
512 | resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.9.tgz#f2d14df87b0739041bc53a7d75e3d77d726a3ec0" | ||
513 | integrity sha512-Nha5b+jmBI271jdTMwrHiNXM+DvThjHOfyZtMX9kj/c/LUj2xiLHsG/1L3tJ8DjAoQN48cHwUwtqBotjyXaSdQ== | ||
514 | |||
503 | "@types/q@^0.0.32": | 515 | "@types/q@^0.0.32": |
504 | version "0.0.32" | 516 | version "0.0.32" |
505 | resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5" | 517 | resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5" |
506 | integrity sha1-vShOV8hPEyXacCur/IKlMoGQwMU= | 518 | integrity sha1-vShOV8hPEyXacCur/IKlMoGQwMU= |
507 | 519 | ||
520 | "@types/react@*": | ||
521 | version "16.8.3" | ||
522 | resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.3.tgz#7b67956f682bea30a5a09b3242c0784ff196c848" | ||
523 | integrity sha512-PjPocAxL9SNLjYMP4dfOShW/rj9FDBJGu3JFRt0zEYf77xfihB6fq8zfDpMrV6s82KnAi7F1OEe5OsQX25Ybdw== | ||
524 | dependencies: | ||
525 | "@types/prop-types" "*" | ||
526 | csstype "^2.2.0" | ||
527 | |||
508 | "@types/sanitize-html@1.18.0": | 528 | "@types/sanitize-html@1.18.0": |
509 | version "1.18.0" | 529 | version "1.18.0" |
510 | resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-1.18.0.tgz#de5cb560a41308ea8474e93b9d10bbb4050692f5" | 530 | resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-1.18.0.tgz#de5cb560a41308ea8474e93b9d10bbb4050692f5" |
@@ -2611,6 +2631,11 @@ cssstyle@^1.0.0: | |||
2611 | dependencies: | 2631 | dependencies: |
2612 | cssom "0.3.x" | 2632 | cssom "0.3.x" |
2613 | 2633 | ||
2634 | csstype@^2.2.0: | ||
2635 | version "2.6.2" | ||
2636 | resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.2.tgz#3043d5e065454579afc7478a18de41909c8a2f01" | ||
2637 | integrity sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow== | ||
2638 | |||
2614 | currently-unhandled@^0.4.1: | 2639 | currently-unhandled@^0.4.1: |
2615 | version "0.4.1" | 2640 | version "0.4.1" |
2616 | resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" | 2641 | resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" |
@@ -9927,12 +9952,7 @@ typedarray@^0.0.6: | |||
9927 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" | 9952 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" |
9928 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= | 9953 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= |
9929 | 9954 | ||
9930 | typescript@3.1.6: | 9955 | typescript@3.2, typescript@3.2.4: |
9931 | version "3.1.6" | ||
9932 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68" | ||
9933 | integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA== | ||
9934 | |||
9935 | typescript@3.2.4: | ||
9936 | version "3.2.4" | 9956 | version "3.2.4" |
9937 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d" | 9957 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d" |
9938 | integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg== | 9958 | integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg== |