aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-01-19 14:52:27 +0100
committerChocobozzz <me@florianbigard.com>2023-01-19 14:52:27 +0100
commit789ba349318e7ff012590491e76087a1204cccd4 (patch)
tree69837d320c77260a188b9ee8f59505e540ad505e
parenta4927884b224e3d4d78ff5db88cbb77bbdf8ee0b (diff)
downloadPeerTube-789ba349318e7ff012590491e76087a1204cccd4.tar.gz
PeerTube-789ba349318e7ff012590491e76087a1204cccd4.tar.zst
PeerTube-789ba349318e7ff012590491e76087a1204cccd4.zip
Support mailto links for custom markup
-rw-r--r--client/src/app/+about/about-instance/about-instance.component.html40
-rw-r--r--client/src/app/+about/about-instance/about-instance.component.ts41
-rw-r--r--client/src/app/+about/about-instance/about-instance.resolver.ts25
-rw-r--r--client/src/app/shared/shared-custom-markup/custom-markup-container.component.ts20
-rw-r--r--client/src/app/shared/shared-instance/instance.service.ts7
5 files changed, 74 insertions, 59 deletions
diff --git a/client/src/app/+about/about-instance/about-instance.component.html b/client/src/app/+about/about-instance/about-instance.component.html
index b113df82f..fdd6157e5 100644
--- a/client/src/app/+about/about-instance/about-instance.component.html
+++ b/client/src/app/+about/about-instance/about-instance.component.html
@@ -21,7 +21,7 @@
21 21
22 <div class="anchor" id="administrators-and-sustainability"></div> 22 <div class="anchor" id="administrators-and-sustainability"></div>
23 <a 23 <a
24 *ngIf="html.administrator || html.maintenanceLifetime || html.businessModel" 24 *ngIf="aboutHTML.administrator || aboutHTML.maintenanceLifetime || aboutHTML.businessModel"
25 class="anchor-link" 25 class="anchor-link"
26 routerLink="/about/instance" 26 routerLink="/about/instance"
27 fragment="administrators-and-sustainability" 27 fragment="administrators-and-sustainability"
@@ -33,7 +33,7 @@
33 </h2> 33 </h2>
34 </a> 34 </a>
35 35
36 <div class="block administrator" *ngIf="html.administrator"> 36 <div class="block administrator" *ngIf="aboutHTML.administrator">
37 <div class="anchor" id="administrators"></div> 37 <div class="anchor" id="administrators"></div>
38 <a 38 <a
39 class="anchor-link" 39 class="anchor-link"
@@ -44,10 +44,10 @@
44 <h3 i18n class="section-title">Who we are</h3> 44 <h3 i18n class="section-title">Who we are</h3>
45 </a> 45 </a>
46 46
47 <div [innerHTML]="html.administrator"></div> 47 <div [innerHTML]="aboutHTML.administrator"></div>
48 </div> 48 </div>
49 49
50 <div class="block creation-reason" *ngIf="html.creationReason"> 50 <div class="block creation-reason" *ngIf="aboutHTML.creationReason">
51 <div class="anchor" id="creation-reason"></div> 51 <div class="anchor" id="creation-reason"></div>
52 <a 52 <a
53 class="anchor-link" 53 class="anchor-link"
@@ -58,10 +58,10 @@
58 <h3 i18n class="section-title">Why we created this instance</h3> 58 <h3 i18n class="section-title">Why we created this instance</h3>
59 </a> 59 </a>
60 60
61 <div [innerHTML]="html.creationReason"></div> 61 <div [innerHTML]="aboutHTML.creationReason"></div>
62 </div> 62 </div>
63 63
64 <div class="block maintenance-lifetime" *ngIf="html.maintenanceLifetime"> 64 <div class="block maintenance-lifetime" *ngIf="aboutHTML.maintenanceLifetime">
65 <div class="anchor" id="maintenance-lifetime"></div> 65 <div class="anchor" id="maintenance-lifetime"></div>
66 <a 66 <a
67 class="anchor-link" 67 class="anchor-link"
@@ -72,10 +72,10 @@
72 <h3 i18n class="section-title">How long we plan to maintain this instance</h3> 72 <h3 i18n class="section-title">How long we plan to maintain this instance</h3>
73 </a> 73 </a>
74 74
75 <div [innerHTML]="html.maintenanceLifetime"></div> 75 <div [innerHTML]="aboutHTML.maintenanceLifetime"></div>
76 </div> 76 </div>
77 77
78 <div class="block business-model" *ngIf="html.businessModel"> 78 <div class="block business-model" *ngIf="aboutHTML.businessModel">
79 <div class="anchor" id="business-model"></div> 79 <div class="anchor" id="business-model"></div>
80 <a 80 <a
81 class="anchor-link" 81 class="anchor-link"
@@ -86,12 +86,12 @@
86 <h3 i18n class="section-title">How we will pay for keeping our instance running</h3> 86 <h3 i18n class="section-title">How we will pay for keeping our instance running</h3>
87 </a> 87 </a>
88 88
89 <div [innerHTML]="html.businessModel"></div> 89 <div [innerHTML]="aboutHTML.businessModel"></div>
90 </div> 90 </div>
91 91
92 <div class="anchor" id="information"></div> 92 <div class="anchor" id="information"></div>
93 <a 93 <a
94 *ngIf="descriptionContent" 94 *ngIf="descriptionElement"
95 class="anchor-link" 95 class="anchor-link"
96 routerLink="/about/instance" 96 routerLink="/about/instance"
97 fragment="information" 97 fragment="information"
@@ -113,13 +113,13 @@
113 <h3 i18n class="section-title">Description</h3> 113 <h3 i18n class="section-title">Description</h3>
114 </a> 114 </a>
115 115
116 <my-custom-markup-container [content]="descriptionContent"></my-custom-markup-container> 116 <my-custom-markup-container [content]="descriptionElement"></my-custom-markup-container>
117 </div> 117 </div>
118 118
119 <div myPluginSelector pluginSelectorId="about-instance-moderation"> 119 <div myPluginSelector pluginSelectorId="about-instance-moderation">
120 <div class="anchor" id="moderation"></div> 120 <div class="anchor" id="moderation"></div>
121 <a 121 <a
122 *ngIf="html.moderationInformation || html.codeOfConduct || html.terms" 122 *ngIf="aboutHTML.moderationInformation || aboutHTML.codeOfConduct || aboutHTML.terms"
123 class="anchor-link" 123 class="anchor-link"
124 routerLink="/about/instance" 124 routerLink="/about/instance"
125 fragment="moderation" 125 fragment="moderation"
@@ -130,7 +130,7 @@
130 </h2> 130 </h2>
131 </a> 131 </a>
132 132
133 <div class="block moderation-information" *ngIf="html.moderationInformation"> 133 <div class="block moderation-information" *ngIf="aboutHTML.moderationInformation">
134 <div class="anchor" id="moderation-information"></div> 134 <div class="anchor" id="moderation-information"></div>
135 <a 135 <a
136 class="anchor-link" 136 class="anchor-link"
@@ -141,10 +141,10 @@
141 <h3 i18n class="section-title">Moderation information</h3> 141 <h3 i18n class="section-title">Moderation information</h3>
142 </a> 142 </a>
143 143
144 <div [innerHTML]="html.moderationInformation"></div> 144 <div [innerHTML]="aboutHTML.moderationInformation"></div>
145 </div> 145 </div>
146 146
147 <div class="block code-of-conduct" *ngIf="html.codeOfConduct"> 147 <div class="block code-of-conduct" *ngIf="aboutHTML.codeOfConduct">
148 <div class="anchor" id="code-of-conduct"></div> 148 <div class="anchor" id="code-of-conduct"></div>
149 <a 149 <a
150 class="anchor-link" 150 class="anchor-link"
@@ -155,7 +155,7 @@
155 <h3 i18n class="section-title">Code of conduct</h3> 155 <h3 i18n class="section-title">Code of conduct</h3>
156 </a> 156 </a>
157 157
158 <div [innerHTML]="html.codeOfConduct"></div> 158 <div [innerHTML]="aboutHTML.codeOfConduct"></div>
159 </div> 159 </div>
160 160
161 <div class="block terms"> 161 <div class="block terms">
@@ -169,14 +169,14 @@
169 <h3 i18n class="section-title">Terms</h3> 169 <h3 i18n class="section-title">Terms</h3>
170 </a> 170 </a>
171 171
172 <div [innerHTML]="html.terms"></div> 172 <div [innerHTML]="aboutHTML.terms"></div>
173 </div> 173 </div>
174 </div> 174 </div>
175 175
176 <div myPluginSelector pluginSelectorId="about-instance-other-information"> 176 <div myPluginSelector pluginSelectorId="about-instance-other-information">
177 <div class="anchor" id="other-information"></div> 177 <div class="anchor" id="other-information"></div>
178 <a 178 <a
179 *ngIf="html.hardwareInformation" 179 *ngIf="aboutHTML.hardwareInformation"
180 class="anchor-link" 180 class="anchor-link"
181 routerLink="/about/instance" 181 routerLink="/about/instance"
182 fragment="other-information" 182 fragment="other-information"
@@ -187,7 +187,7 @@
187 </h2> 187 </h2>
188 </a> 188 </a>
189 189
190 <div class="block hardware-information" *ngIf="html.hardwareInformation"> 190 <div class="block hardware-information" *ngIf="aboutHTML.hardwareInformation">
191 <div class="anchor" id="hardware-information"></div> 191 <div class="anchor" id="hardware-information"></div>
192 <a 192 <a
193 class="anchor-link" 193 class="anchor-link"
@@ -198,7 +198,7 @@
198 <h3 i18n class="section-title">Hardware information</h3> 198 <h3 i18n class="section-title">Hardware information</h3>
199 </a> 199 </a>
200 200
201 <div [innerHTML]="html.hardwareInformation"></div> 201 <div [innerHTML]="aboutHTML.hardwareInformation"></div>
202 </div> 202 </div>
203 </div> 203 </div>
204 </div> 204 </div>
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 0826bbc5a..e1501d7de 100644
--- a/client/src/app/+about/about-instance/about-instance.component.ts
+++ b/client/src/app/+about/about-instance/about-instance.component.ts
@@ -2,7 +2,7 @@ import { ViewportScroller } from '@angular/common'
2import { AfterViewChecked, Component, ElementRef, OnInit, ViewChild } from '@angular/core' 2import { AfterViewChecked, Component, ElementRef, OnInit, ViewChild } from '@angular/core'
3import { ActivatedRoute } from '@angular/router' 3import { ActivatedRoute } from '@angular/router'
4import { Notifier, ServerService } from '@app/core' 4import { Notifier, ServerService } from '@app/core'
5import { InstanceService } from '@app/shared/shared-instance' 5import { AboutHTML } from '@app/shared/shared-instance'
6import { copyToClipboard } from '@root-helpers/utils' 6import { copyToClipboard } from '@root-helpers/utils'
7import { HTMLServerConfig } from '@shared/models/server' 7import { HTMLServerConfig } from '@shared/models/server'
8import { ResolverData } from './about-instance.resolver' 8import { ResolverData } from './about-instance.resolver'
@@ -17,22 +17,12 @@ export class AboutInstanceComponent implements OnInit, AfterViewChecked {
17 @ViewChild('descriptionWrapper') descriptionWrapper: ElementRef<HTMLInputElement> 17 @ViewChild('descriptionWrapper') descriptionWrapper: ElementRef<HTMLInputElement>
18 @ViewChild('contactAdminModal', { static: true }) contactAdminModal: ContactAdminModalComponent 18 @ViewChild('contactAdminModal', { static: true }) contactAdminModal: ContactAdminModalComponent
19 19
20 shortDescription = '' 20 aboutHTML: AboutHTML
21 descriptionContent: string 21 descriptionElement: HTMLDivElement
22
23 html = {
24 terms: '',
25 codeOfConduct: '',
26 moderationInformation: '',
27 administrator: '',
28 creationReason: '',
29 maintenanceLifetime: '',
30 businessModel: '',
31 hardwareInformation: ''
32 }
33 22
34 languages: string[] = [] 23 languages: string[] = []
35 categories: string[] = [] 24 categories: string[] = []
25 shortDescription = ''
36 26
37 initialized = false 27 initialized = false
38 28
@@ -44,8 +34,7 @@ export class AboutInstanceComponent implements OnInit, AfterViewChecked {
44 private viewportScroller: ViewportScroller, 34 private viewportScroller: ViewportScroller,
45 private route: ActivatedRoute, 35 private route: ActivatedRoute,
46 private notifier: Notifier, 36 private notifier: Notifier,
47 private serverService: ServerService, 37 private serverService: ServerService
48 private instanceService: InstanceService
49 ) {} 38 ) {}
50 39
51 get instanceName () { 40 get instanceName () {
@@ -60,8 +49,16 @@ export class AboutInstanceComponent implements OnInit, AfterViewChecked {
60 return this.serverConfig.instance.isNSFW 49 return this.serverConfig.instance.isNSFW
61 } 50 }
62 51
63 async ngOnInit () { 52 ngOnInit () {
64 const { about, languages, categories }: ResolverData = this.route.snapshot.data.instanceData 53 const { about, languages, categories, aboutHTML, descriptionElement }: ResolverData = this.route.snapshot.data.instanceData
54
55 this.aboutHTML = aboutHTML
56 this.descriptionElement = descriptionElement
57
58 this.languages = languages
59 this.categories = categories
60
61 this.shortDescription = about.instance.shortDescription
65 62
66 this.serverConfig = this.serverService.getHTMLConfig() 63 this.serverConfig = this.serverService.getHTMLConfig()
67 64
@@ -73,14 +70,6 @@ export class AboutInstanceComponent implements OnInit, AfterViewChecked {
73 this.contactAdminModal.show(prefill) 70 this.contactAdminModal.show(prefill)
74 }) 71 })
75 72
76 this.languages = languages
77 this.categories = categories
78
79 this.shortDescription = about.instance.shortDescription
80 this.descriptionContent = about.instance.description
81
82 this.html = await this.instanceService.buildHtml(about)
83
84 this.initialized = true 73 this.initialized = true
85 } 74 }
86 75
diff --git a/client/src/app/+about/about-instance/about-instance.resolver.ts b/client/src/app/+about/about-instance/about-instance.resolver.ts
index ee0219df0..8818fc582 100644
--- a/client/src/app/+about/about-instance/about-instance.resolver.ts
+++ b/client/src/app/+about/about-instance/about-instance.resolver.ts
@@ -2,16 +2,25 @@ import { forkJoin } from 'rxjs'
2import { map, switchMap } from 'rxjs/operators' 2import { map, switchMap } from 'rxjs/operators'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { Resolve } from '@angular/router' 4import { Resolve } from '@angular/router'
5import { InstanceService } from '@app/shared/shared-instance' 5import { CustomMarkupService } from '@app/shared/shared-custom-markup'
6import { AboutHTML, InstanceService } from '@app/shared/shared-instance'
6import { About } from '@shared/models/server' 7import { About } from '@shared/models/server'
7 8
8export type ResolverData = { about: About, languages: string[], categories: string[] } 9export type ResolverData = {
10 about: About
11 languages: string[]
12 categories: string[]
13 aboutHTML: AboutHTML
14 descriptionElement: HTMLDivElement
15}
9 16
10@Injectable() 17@Injectable()
11export class AboutInstanceResolver implements Resolve<any> { 18export class AboutInstanceResolver implements Resolve<any> {
12 19
13 constructor ( 20 constructor (
14 private instanceService: InstanceService 21 private instanceService: InstanceService,
22 private customMarkupService: CustomMarkupService
23
15 ) {} 24 ) {}
16 25
17 resolve () { 26 resolve () {
@@ -19,9 +28,15 @@ export class AboutInstanceResolver implements Resolve<any> {
19 .pipe( 28 .pipe(
20 switchMap(about => { 29 switchMap(about => {
21 return forkJoin([ 30 return forkJoin([
31 Promise.resolve(about),
22 this.instanceService.buildTranslatedLanguages(about), 32 this.instanceService.buildTranslatedLanguages(about),
23 this.instanceService.buildTranslatedCategories(about) 33 this.instanceService.buildTranslatedCategories(about),
24 ]).pipe(map(([ languages, categories ]) => ({ about, languages, categories }) as ResolverData)) 34 this.instanceService.buildHtml(about),
35 this.customMarkupService.buildElement(about.instance.description)
36 ])
37 }),
38 map(([ about, languages, categories, aboutHTML, { rootElement } ]) => {
39 return { about, languages, categories, aboutHTML, descriptionElement: rootElement } as ResolverData
25 }) 40 })
26 ) 41 )
27 } 42 }
diff --git a/client/src/app/shared/shared-custom-markup/custom-markup-container.component.ts b/client/src/app/shared/shared-custom-markup/custom-markup-container.component.ts
index 4e802b14d..b2ee2d8f2 100644
--- a/client/src/app/shared/shared-custom-markup/custom-markup-container.component.ts
+++ b/client/src/app/shared/shared-custom-markup/custom-markup-container.component.ts
@@ -6,9 +6,9 @@ import { CustomMarkupService } from './custom-markup.service'
6 templateUrl: './custom-markup-container.component.html' 6 templateUrl: './custom-markup-container.component.html'
7}) 7})
8export class CustomMarkupContainerComponent implements OnChanges { 8export class CustomMarkupContainerComponent implements OnChanges {
9 @ViewChild('contentWrapper') contentWrapper: ElementRef<HTMLInputElement> 9 @ViewChild('contentWrapper', { static: true }) contentWrapper: ElementRef<HTMLInputElement>
10 10
11 @Input() content: string 11 @Input() content: string | HTMLDivElement
12 12
13 displayed = false 13 displayed = false
14 14
@@ -17,17 +17,23 @@ export class CustomMarkupContainerComponent implements OnChanges {
17 ) { } 17 ) { }
18 18
19 async ngOnChanges () { 19 async ngOnChanges () {
20 await this.buildElement() 20 await this.rebuild()
21 } 21 }
22 22
23 private async buildElement () { 23 private async rebuild () {
24 if (!this.content) return 24 if (this.content instanceof HTMLDivElement) {
25 return this.loadElement(this.content)
26 }
25 27
26 const { rootElement, componentsLoaded } = await this.customMarkupService.buildElement(this.content) 28 const { rootElement, componentsLoaded } = await this.customMarkupService.buildElement(this.content)
27 this.contentWrapper.nativeElement.appendChild(rootElement)
28
29 await componentsLoaded 29 await componentsLoaded
30 30
31 return this.loadElement(rootElement)
32 }
33
34 private loadElement (el: HTMLDivElement) {
35 this.contentWrapper.nativeElement.appendChild(el)
36
31 this.displayed = true 37 this.displayed = true
32 } 38 }
33} 39}
diff --git a/client/src/app/shared/shared-instance/instance.service.ts b/client/src/app/shared/shared-instance/instance.service.ts
index 89f47db24..f5b2e05db 100644
--- a/client/src/app/shared/shared-instance/instance.service.ts
+++ b/client/src/app/shared/shared-instance/instance.service.ts
@@ -7,6 +7,11 @@ import { peertubeTranslate } from '@shared/core-utils/i18n'
7import { About } from '@shared/models' 7import { About } from '@shared/models'
8import { environment } from '../../../environments/environment' 8import { environment } from '../../../environments/environment'
9 9
10export type AboutHTML = Pick<About['instance'],
11'terms' | 'codeOfConduct' | 'moderationInformation' | 'administrator' | 'creationReason' |
12'maintenanceLifetime' | 'businessModel' | 'hardwareInformation'
13>
14
10@Injectable() 15@Injectable()
11export class InstanceService { 16export class InstanceService {
12 private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config' 17 private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config'
@@ -39,7 +44,7 @@ export class InstanceService {
39 } 44 }
40 45
41 async buildHtml (about: About) { 46 async buildHtml (about: About) {
42 const html = { 47 const html: AboutHTML = {
43 terms: '', 48 terms: '',
44 codeOfConduct: '', 49 codeOfConduct: '',
45 moderationInformation: '', 50 moderationInformation: '',