From ccc00cb2aac1360921b957f3ecb3be7eb55dfa1b Mon Sep 17 00:00:00 2001 From: Chocobozzz <me@florianbigard.com> Date: Fri, 23 Aug 2019 15:23:27 +0200 Subject: Add more attributes to about page --- .../about-instance/about-instance.component.html | 53 +++++++- .../about-instance/about-instance.component.scss | 19 ++- .../about-instance/about-instance.component.ts | 27 ++++- .../edit-custom-config.component.html | 135 +++++++++++++++++---- .../edit-custom-config.component.scss | 9 ++ .../edit-custom-config.component.ts | 68 +++++++++-- .../my-account-video-settings.component.html | 2 +- .../my-account-video-settings.component.ts | 47 +++---- client/src/app/+my-account/my-account.module.ts | 4 +- client/src/app/shared/shared.module.ts | 7 +- 10 files changed, 290 insertions(+), 81 deletions(-) (limited to 'client/src') 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 7c27ec760..0fd3626b7 100644 --- a/client/src/app/+about/about-instance/about-instance.component.html +++ b/client/src/app/+about/about-instance/about-instance.component.html @@ -1,7 +1,8 @@ <div class="row"> <div class="col-md-12 col-xl-6"> + <div class="about-instance-title"> - <div i18n>About {{ instanceName }} instance</div> + <div i18n class="title">About {{ instanceName }} instance</div> <div *ngIf="isContactFormEnabled" (click)="openContactModal()" i18n role="button" class="contact-admin">Contact administrator</div> </div> @@ -12,16 +13,58 @@ <div *ngIf="isNSFW" class="dedicated-to-nsfw">This instance is dedicated to sensitive/NSFW content.</div> </div> - <div class="description"> + <div class="middle-title" *ngIf="html.administrator || maintenanceLifetime || businessModel"> + Administrators & sustainability + </div> + + <div class="block administrator" *ngIf="html.administrator"> + <div i18n class="section-title">Instance administrators</div> + + <div [innerHTML]="html.administrator"></div> + </div> + + <div class="block maintenance-lifetime" *ngIf="maintenanceLifetime"> + <div i18n class="section-title">Maintenance lifetime</div> + + <p>{{ maintenanceLifetime }}</p> + </div> + + <div class="block business-model" *ngIf="businessModel"> + <div i18n class="section-title">Business model</div> + + <p>{{ businessModel }}</p> + </div> + + <div class="middle-title" *ngIf="html.description"> + Information + </div> + + <div class="block description"> <div i18n class="section-title">Description</div> - <div [innerHTML]="descriptionHTML"></div> + <div [innerHTML]="html.description"></div> + </div> + + <div class="middle-title" *ngIf="html.moderationInformation || html.codeOfConduct || html.terms"> + Moderation + </div> + + <div class="block moderation-information" *ngIf="html.moderationInformation"> + <div i18n class="section-title">Moderation information</div> + + <div [innerHTML]="html.moderationInformation"></div> + </div> + + <div class="block code-of-conduct" *ngIf="html.codeOfConduct"> + <div i18n class="section-title">Code of conduct</div> + + <div [innerHTML]="html.codeOfConduct"></div> </div> - <div class="terms" id="terms-section"> + <div class="block terms" id="terms-section"> <div i18n class="section-title">Terms</div> - <div [innerHTML]="termsHTML"></div> + <div [innerHTML]="html.terms"></div> </div> </div> diff --git a/client/src/app/+about/about-instance/about-instance.component.scss b/client/src/app/+about/about-instance/about-instance.component.scss index 0296ae8e9..0585ad5f3 100644 --- a/client/src/app/+about/about-instance/about-instance.component.scss +++ b/client/src/app/+about/about-instance/about-instance.component.scss @@ -5,13 +5,13 @@ display: flex; justify-content: space-between; - & > div { + .title { font-size: 20px; - font-weight: bold; margin-bottom: 15px; + font-weight: $font-semibold; } - & > .contact-admin { + .contact-admin { @include peertube-button; @include orange-button; @@ -21,11 +21,20 @@ .section-title { font-weight: $font-semibold; - font-size: 20px; + font-size: 16px; margin-bottom: 5px; + display: flex; + align-items: center; +} + +.middle-title { + @include in-content-small-title; + + margin-top: 45px; + margin-bottom: 25px; } -.short-description, .description, .terms, .signup { +.block { margin-bottom: 30px; } 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 a5204de27..b85a6be94 100644 --- a/client/src/app/+about/about-instance/about-instance.component.ts +++ b/client/src/app/+about/about-instance/about-instance.component.ts @@ -14,8 +14,20 @@ export class AboutInstanceComponent implements OnInit { @ViewChild('contactAdminModal', { static: true }) contactAdminModal: ContactAdminModalComponent shortDescription = '' - descriptionHTML = '' - termsHTML = '' + + html = { + description: '', + terms: '', + codeOfConduct: '', + moderationInformation: '', + administrator: '' + } + + maintenanceLifetime = '' + businessModel = '' + + languages: string[] = [] + categories: number[] = [] constructor ( private notifier: Notifier, @@ -43,8 +55,15 @@ export class AboutInstanceComponent implements OnInit { async res => { this.shortDescription = res.instance.shortDescription - this.descriptionHTML = await this.markdownService.textMarkdownToHTML(res.instance.description) - this.termsHTML = await this.markdownService.textMarkdownToHTML(res.instance.terms) + this.maintenanceLifetime = res.instance.maintenanceLifetime + this.businessModel = res.instance.businessModel + + for (const key of [ 'description', 'terms', 'codeOfConduct', 'moderationInformation', 'administrator' ]) { + this.html[key] = await this.markdownService.textMarkdownToHTML(res.instance[key]) + } + + this.languages = res.instance.languages + this.categories = res.instance.categories }, () => this.notifier.error(this.i18n('Cannot get about information from server')) 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 ec6f879d7..50df8a8ac 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,12 +2,13 @@ <ngb-tabset class="root-tabset bootstrap"> - <ngb-tab i18n-title title="Basic configuration"> + <ngb-tab i18n-title title="Instance information"> <ng-template ngbTabContent> - <div i18n class="inner-form-title">Instance</div> - <ng-container formGroupName="instance"> + + <div i18n class="inner-form-title">Instance</div> + <div class="form-group"> <label i18n for="instanceName">Name</label> <input @@ -36,36 +37,40 @@ </div> <div class="form-group"> - <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> - <my-markdown-textarea - id="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true" - [ngClass]="{ 'input-error': formErrors['instance.terms'] }" - ></my-markdown-textarea> - <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div> + <label i18n for="instanceCategories">Main instance categories</label> + + <div> + <p-multiSelect + inputId="instanceCategories" [options]="categoryItems" formControlName="categories" showToggleAll="false" + [defaultLabel]="getDefaultCategoryLabel()" [selectedItemsLabel]="getSelectedCategoryLabel()" + emptyFilterMessage="No results found" i18n-emptyFilterMessage + ></p-multiSelect> + </div> </div> + <div class="form-group"> + <label i18n for="instanceLanguages">Main languages you/your moderators speak</label> + + <div> + <p-multiSelect + inputId="instanceLanguages" [options]="languageItems" formControlName="languages" showToggleAll="false" + [defaultLabel]="getDefaultLanguageLabel()" [selectedItemsLabel]="getSelectedLanguageLabel()" + emptyFilterMessage="No results found" i18n-emptyFilterMessage + ></p-multiSelect> + </div> + </div> + + <div i18n class="inner-form-title">Moderation & NSFW</div> + <div class="form-group"> <my-peertube-checkbox inputName="instanceIsNSFW" formControlName="isNSFW" - i18n-labelText labelText="Dedicated to sensitive or NSFW content" + i18n-labelText labelText="This instance is dedicated to sensitive or NSFW content" i18n-helpHtml helpHtml="Enabling it will allow other administrators to know that you are mainly federating sensitive content.<br /><br /> Moreover, the NSFW checkbox on video upload will be automatically checked by default." ></my-peertube-checkbox> </div> - <div class="form-group"> - <label i18n for="instanceDefaultClientRoute">Default client route</label> - <div class="peertube-select-container"> - <select id="instanceDefaultClientRoute" formControlName="defaultClientRoute"> - <option i18n value="/videos/overview">Videos Overview</option> - <option i18n value="/videos/trending">Videos Trending</option> - <option i18n value="/videos/recently-added">Videos Recently Added</option> - <option i18n value="/videos/local">Local videos</option> - </select> - </div> - <div *ngIf="formErrors.instance.defaultClientRoute" class="form-error">{{ formErrors.instance.defaultClientRoute }}</div> - </div> - <div class="form-group"> <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label> <my-help @@ -82,10 +87,79 @@ </div> <div *ngIf="formErrors.instance.defaultNSFWPolicy" class="form-error">{{ formErrors.instance.defaultNSFWPolicy }}</div> </div> + + <div class="form-group"> + <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> + <my-markdown-textarea + id="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true" + [ngClass]="{ 'input-error': formErrors['instance.terms'] }" + ></my-markdown-textarea> + <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div> + </div> + + <div class="form-group"> + <label i18n for="instanceCodeOfConduct">Code of conduct</label><my-help helpType="markdownText"></my-help> + <my-markdown-textarea + id="instanceCodeOfConduct" formControlName="codeOfConduct" textareaWidth="500px" [previewColumn]="true" + [ngClass]="{ 'input-error': formErrors['instance.codeOfConduct'] }" + ></my-markdown-textarea> + <div *ngIf="formErrors.instance.codeOfConduct" class="form-error">{{ formErrors.instance.codeOfConduct }}</div> + </div> + + <div class="form-group"> + <label i18n for="instanceModerationInformation">Moderation information</label><my-help helpType="markdownText"></my-help> + <div class="label-small-info">Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc</div> + + <my-markdown-textarea + id="instanceModerationInformation" formControlName="moderationInformation" textareaWidth="500px" [previewColumn]="true" + [ngClass]="{ 'input-error': formErrors['instance.moderationInformation'] }" + ></my-markdown-textarea> + <div *ngIf="formErrors.instance.moderationInformation" class="form-error">{{ formErrors.instance.moderationInformation }}</div> + </div> + + <div i18n class="inner-form-title">You and your instance</div> + + <div class="form-group"> + <label i18n for="instanceAdministrator">Who is behind the instance? </label> + <div class="label-small-info">A single person? A non profit? A company?</div> + + <textarea + id="instanceAdministrator" formControlName="administrator" + [ngClass]="{ 'input-error': formErrors['instance.administrator'] }" + ></textarea> + <div *ngIf="formErrors.instance.administrator" class="form-error">{{ formErrors.instance.administrator }}</div> + </div> + + <div class="form-group"> + <label i18n for="instanceMaintenanceLifetime">How long do you plan to maintain this instance?</label> + <div class="label-small-info">It's important to know for users who want to register on your instance</div> + + <textarea + id="instanceMaintenanceLifetime" formControlName="maintenanceLifetime" + [ngClass]="{ 'input-error': formErrors['instance.maintenanceLifetime'] }" + ></textarea> + <div *ngIf="formErrors.instance.maintenanceLifetime" class="form-error">{{ formErrors.instance.maintenanceLifetime }}</div> + </div> + + <div class="form-group"> + <label i18n for="instanceBusinessModel">How will you pay the PeerTube instance server?</label> + <div class="label-small-info">With you own funds? With users donations? Advertising?</div> + + <textarea + id="instanceBusinessModel" formControlName="businessModel" + [ngClass]="{ 'input-error': formErrors['instance.businessModel'] }" + ></textarea> + <div *ngIf="formErrors.instance.businessModel" class="form-error">{{ formErrors.instance.businessModel }}</div> + </div> + </ng-container> + </ng-template> + </ngb-tab> + <ngb-tab i18n-title title="Basic configuration"> + <ng-template ngbTabContent> - <div i18n class="inner-form-title">Theme</div> + <div i18n class="inner-form-title">Theme & Default route</div> <ng-container formGroupName="theme"> <div class="form-group"> @@ -102,6 +176,19 @@ </ng-container> + <div class="form-group" formGroupName="instance"> + <label i18n for="instanceDefaultClientRoute">Default client route</label> + <div class="peertube-select-container"> + <select id="instanceDefaultClientRoute" formControlName="defaultClientRoute"> + <option i18n value="/videos/overview">Videos Discover</option> + <option i18n value="/videos/trending">Videos Trending</option> + <option i18n value="/videos/recently-added">Videos Recently Added</option> + <option i18n value="/videos/local">Local videos</option> + </select> + </div> + <div *ngIf="formErrors.instance.defaultClientRoute" class="form-error">{{ formErrors.instance.defaultClientRoute }}</div> + </div> + <div i18n class="inner-form-title">Signup</div> <ng-container formGroupName="signup"> diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss index c90bd5141..68f1b01b7 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss @@ -1,6 +1,10 @@ @import '_variables'; @import '_mixins'; +.form-group { + margin-bottom: 25px; +} + input[type=text] { @include peertube-input-text(340px); display: block; @@ -44,3 +48,8 @@ textarea { height: 100px; } } + +.label-small-info { + font-style: italic; + margin-bottom: 10px; +} 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 d51104569..3119ab040 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 @@ -6,6 +6,9 @@ import { Notifier } from '@app/core' import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model' import { I18n } from '@ngx-translate/i18n-polyfill' import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' +import { SelectItem } from 'primeng/api' +import { forkJoin } from 'rxjs' +import { first } from 'rxjs/operators' @Component({ selector: 'my-edit-custom-config', @@ -18,6 +21,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { resolutions: { id: string, label: string }[] = [] transcodingThreadOptions: { label: string, value: number }[] = [] + languageItems: SelectItem[] = [] + categoryItems: SelectItem[] = [] + constructor ( protected formValidatorService: FormValidatorService, private customConfigValidatorsService: CustomConfigValidatorsService, @@ -88,10 +94,22 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { name: this.customConfigValidatorsService.INSTANCE_NAME, shortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION, description: null, - terms: null, - defaultClientRoute: null, + isNSFW: false, defaultNSFWPolicy: null, + + terms: null, + codeOfConduct: null, + moderationInformation: null, + administrator: null, + maintenanceLifetime: null, + businessModel: null, + + categories: null, + languages: null, + + defaultClientRoute: null, + customizations: { javascript: null, css: null @@ -184,18 +202,27 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { this.buildForm(formGroupData) - this.configService.getCustomConfig() - .subscribe( - res => { - this.customConfig = res + forkJoin([ + this.configService.getCustomConfig(), + this.serverService.videoLanguagesLoaded.pipe(first()), // First so the observable completes + this.serverService.videoCategoriesLoaded.pipe(first()) + ]).subscribe( + ([ config ]) => { + this.customConfig = config - this.updateForm() - // Force form validation - this.forceCheck() - }, + const languages = this.serverService.getVideoLanguages() + this.languageItems = languages.map(l => ({ label: l.label, value: l.id })) - err => this.notifier.error(err.message) - ) + const categories = this.serverService.getVideoCategories() + this.categoryItems = categories.map(l => ({ label: l.label, value: l.id })) + + this.updateForm() + // Force form validation + this.forceCheck() + }, + + err => this.notifier.error(err.message) + ) } isTranscodingEnabled () { @@ -224,8 +251,23 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { ) } + getSelectedLanguageLabel () { + return this.i18n('{{\'{0} languages selected') + } + + getDefaultLanguageLabel () { + return this.i18n('No language') + } + + getSelectedCategoryLabel () { + return this.i18n('{{\'{0} categories selected') + } + + getDefaultCategoryLabel () { + return this.i18n('No category') + } + private updateForm () { this.form.patchValue(this.customConfig) } - } diff --git a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html index 2796dd2db..caa032149 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html +++ b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html @@ -23,7 +23,7 @@ <div> <p-multiSelect - [options]="languageItems" formControlName="videoLanguages" showToggleAll="true" + inputId="videoLanguages" [options]="languageItems" formControlName="videoLanguages" showToggleAll="true" [defaultLabel]="getDefaultVideoLanguageLabel()" [selectedItemsLabel]="getSelectedVideoLanguageLabel()" emptyFilterMessage="No results found" i18n-emptyFilterMessage ></p-multiSelect> diff --git a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts index 77febf179..4fb828082 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts @@ -5,9 +5,9 @@ import { AuthService } from '../../../core' import { FormReactive, User, UserService } from '../../../shared' import { I18n } from '@ngx-translate/i18n-polyfill' import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' -import { Subject } from 'rxjs' +import { forkJoin, Subject } from 'rxjs' import { SelectItem } from 'primeng/api' -import { switchMap } from 'rxjs/operators' +import { first } from 'rxjs/operators' @Component({ selector: 'my-account-video-settings', @@ -39,30 +39,31 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI videoLanguages: null }) - this.serverService.videoLanguagesLoaded - .pipe(switchMap(() => this.userInformationLoaded)) - .subscribe(() => { - const languages = this.serverService.getVideoLanguages() - - this.languageItems = [ { label: this.i18n('Unknown language'), value: '_unknown' } ] - this.languageItems = this.languageItems - .concat(languages.map(l => ({ label: l.label, value: l.id }))) - - const videoLanguages = this.user.videoLanguages - ? this.user.videoLanguages - : this.languageItems.map(l => l.value) - - this.form.patchValue({ - nsfwPolicy: this.user.nsfwPolicy, - webTorrentEnabled: this.user.webTorrentEnabled, - autoPlayVideo: this.user.autoPlayVideo === true, - videoLanguages - }) - }) + forkJoin([ + this.serverService.videoLanguagesLoaded.pipe(first()), + this.userInformationLoaded.pipe(first()) + ]).subscribe(() => { + const languages = this.serverService.getVideoLanguages() + + this.languageItems = [ { label: this.i18n('Unknown language'), value: '_unknown' } ] + this.languageItems = this.languageItems + .concat(languages.map(l => ({ label: l.label, value: l.id }))) + + const videoLanguages = this.user.videoLanguages + ? this.user.videoLanguages + : this.languageItems.map(l => l.value) + + this.form.patchValue({ + nsfwPolicy: this.user.nsfwPolicy, + webTorrentEnabled: this.user.webTorrentEnabled, + autoPlayVideo: this.user.autoPlayVideo === true, + videoLanguages + }) + }) } updateDetails () { - const nsfwPolicy = this.form.value['nsfwPolicy'] + const nsfwPolicy = this.form.value[ 'nsfwPolicy' ] const webTorrentEnabled = this.form.value['webTorrentEnabled'] const autoPlayVideo = this.form.value['autoPlayVideo'] diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts index 571f46de9..6cf1499d3 100644 --- a/client/src/app/+my-account/my-account.module.ts +++ b/client/src/app/+my-account/my-account.module.ts @@ -37,7 +37,6 @@ import { } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component' import { DragDropModule } from '@angular/cdk/drag-drop' import { MyAccountChangeEmailComponent } from '@app/+my-account/my-account-settings/my-account-change-email' -import { MultiSelectModule } from 'primeng/multiselect' import { MyAccountInterfaceSettingsComponent } from '@app/+my-account/my-account-settings/my-account-interface' @NgModule({ @@ -48,8 +47,7 @@ import { MyAccountInterfaceSettingsComponent } from '@app/+my-account/my-account SharedModule, TableModule, InputSwitchModule, - DragDropModule, - MultiSelectModule + DragDropModule ], declarations: [ diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index eb57a2fff..d71f6357b 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -6,10 +6,8 @@ import { RouterModule } from '@angular/router' import { MarkdownTextareaComponent } from '@app/shared/forms/markdown-textarea.component' import { HelpComponent } from '@app/shared/misc/help.component' import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive' - import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' - import { AUTH_INTERCEPTOR_PROVIDER } from './auth' import { ButtonComponent } from './buttons/button.component' import { DeleteButtonComponent } from './buttons/delete-button.component' @@ -93,6 +91,7 @@ import { VideoDownloadComponent } from '@app/shared/video/modals/video-download. import { VideoReportComponent } from '@app/shared/video/modals/video-report.component' import { ClipboardModule } from 'ngx-clipboard' import { FollowService } from '@app/shared/instance/follow.service' +import { MultiSelectModule } from 'primeng/multiselect' @NgModule({ imports: [ @@ -113,7 +112,8 @@ import { FollowService } from '@app/shared/instance/follow.service' PrimeSharedModule, InputMaskModule, - NgPipesModule + NgPipesModule, + MultiSelectModule ], declarations: [ @@ -186,6 +186,7 @@ import { FollowService } from '@app/shared/instance/follow.service' InputMaskModule, BytesPipe, KeysPipe, + MultiSelectModule, LoaderComponent, SmallLoaderComponent, -- cgit v1.2.3