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