diff options
23 files changed, 242 insertions, 156 deletions
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index da66e4ef0..6969329e8 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html | |||
@@ -61,8 +61,8 @@ | |||
61 | </p-toast> | 61 | </p-toast> |
62 | 62 | ||
63 | <ng-container *ngIf="isUserLoggedIn()"> | 63 | <ng-container *ngIf="isUserLoggedIn()"> |
64 | <my-account-setup-modal #accountSetupModal></my-account-setup-modal> | 64 | <my-account-setup-warning-modal #accountSetupWarningModal></my-account-setup-warning-modal> |
65 | <my-welcome-modal #welcomeModal></my-welcome-modal> | 65 | <my-admin-welcome-modal #adminWelcomeModal></my-admin-welcome-modal> |
66 | <my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal> | 66 | <my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal> |
67 | </ng-container> | 67 | </ng-container> |
68 | 68 | ||
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index ae6046cc1..86b687173 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { Hotkey, HotkeysService } from 'angular2-hotkeys' | 1 | import { Hotkey, HotkeysService } from 'angular2-hotkeys' |
2 | import { filter, map, switchMap } from 'rxjs/operators' | 2 | import { forkJoin } from 'rxjs' |
3 | import { filter, first, map } from 'rxjs/operators' | ||
3 | import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common' | 4 | import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common' |
4 | import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core' | 5 | import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core' |
5 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser' | 6 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser' |
@@ -17,15 +18,15 @@ import { | |||
17 | } from '@app/core' | 18 | } from '@app/core' |
18 | import { HooksService } from '@app/core/plugins/hooks.service' | 19 | import { HooksService } from '@app/core/plugins/hooks.service' |
19 | import { PluginService } from '@app/core/plugins/plugin.service' | 20 | import { PluginService } from '@app/core/plugins/plugin.service' |
21 | import { AccountSetupWarningModalComponent } from '@app/modal/account-setup-warning-modal.component' | ||
20 | import { CustomModalComponent } from '@app/modal/custom-modal.component' | 22 | import { CustomModalComponent } from '@app/modal/custom-modal.component' |
21 | import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component' | 23 | import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component' |
22 | import { WelcomeModalComponent } from '@app/modal/welcome-modal.component' | 24 | import { AdminWelcomeModalComponent } from '@app/modal/admin-welcome-modal.component' |
23 | import { AccountSetupModalComponent } from '@app/modal/account-setup-modal.component' | ||
24 | import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap' | 25 | import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap' |
25 | import { LoadingBarService } from '@ngx-loading-bar/core' | 26 | import { LoadingBarService } from '@ngx-loading-bar/core' |
26 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | 27 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
27 | import { getShortLocale } from '@shared/core-utils/i18n' | 28 | import { getShortLocale } from '@shared/core-utils/i18n' |
28 | import { BroadcastMessageLevel, HTMLServerConfig, ServerConfig, UserRole } from '@shared/models' | 29 | import { BroadcastMessageLevel, HTMLServerConfig, UserRole } from '@shared/models' |
29 | import { MenuService } from './core/menu/menu.service' | 30 | import { MenuService } from './core/menu/menu.service' |
30 | import { POP_STATE_MODAL_DISMISS } from './helpers' | 31 | import { POP_STATE_MODAL_DISMISS } from './helpers' |
31 | import { InstanceService } from './shared/shared-instance' | 32 | import { InstanceService } from './shared/shared-instance' |
@@ -38,8 +39,8 @@ import { InstanceService } from './shared/shared-instance' | |||
38 | export class AppComponent implements OnInit, AfterViewInit { | 39 | export class AppComponent implements OnInit, AfterViewInit { |
39 | private static BROADCAST_MESSAGE_KEY = 'app-broadcast-message-dismissed' | 40 | private static BROADCAST_MESSAGE_KEY = 'app-broadcast-message-dismissed' |
40 | 41 | ||
41 | @ViewChild('accountSetupModal') accountSetupModal: AccountSetupModalComponent | 42 | @ViewChild('accountSetupWarningModal') accountSetupWarningModal: AccountSetupWarningModalComponent |
42 | @ViewChild('welcomeModal') welcomeModal: WelcomeModalComponent | 43 | @ViewChild('adminWelcomeModal') adminWelcomeModal: AdminWelcomeModalComponent |
43 | @ViewChild('instanceConfigWarningModal') instanceConfigWarningModal: InstanceConfigWarningModalComponent | 44 | @ViewChild('instanceConfigWarningModal') instanceConfigWarningModal: InstanceConfigWarningModalComponent |
44 | @ViewChild('customModal') customModal: CustomModalComponent | 45 | @ViewChild('customModal') customModal: CustomModalComponent |
45 | 46 | ||
@@ -221,33 +222,41 @@ export class AppComponent implements OnInit, AfterViewInit { | |||
221 | } | 222 | } |
222 | 223 | ||
223 | private openModalsIfNeeded () { | 224 | private openModalsIfNeeded () { |
224 | this.authService.userInformationLoaded | 225 | const userSub = this.authService.userInformationLoaded |
225 | .pipe( | 226 | .pipe(map(() => this.authService.getUser())) |
226 | map(() => this.authService.getUser()), | 227 | |
227 | filter(user => user.role === UserRole.ADMINISTRATOR), | 228 | // Admin modal |
228 | switchMap(user => { | 229 | userSub.pipe( |
229 | return this.serverService.getConfig() | 230 | filter(user => user.role === UserRole.ADMINISTRATOR) |
230 | .pipe(map(serverConfig => ({ serverConfig, user }))) | 231 | ).subscribe(user => this.openAdminModalsIfNeeded(user)) |
231 | }) | 232 | |
232 | ).subscribe(({ serverConfig, user }) => this._openAdminModalsIfNeeded(serverConfig, user)) | 233 | // Account modal |
234 | userSub.pipe( | ||
235 | filter(user => user.role !== UserRole.ADMINISTRATOR) | ||
236 | ).subscribe(user => this.openAccountModalsIfNeeded(user)) | ||
233 | } | 237 | } |
234 | 238 | ||
235 | private _openAdminModalsIfNeeded (serverConfig: ServerConfig, user: User) { | 239 | private openAdminModalsIfNeeded (user: User) { |
236 | if (user.noWelcomeModal !== true) return this.welcomeModal.show() | 240 | if (this.adminWelcomeModal.shouldOpen(user)) { |
237 | 241 | return this.adminWelcomeModal.show() | |
238 | if (user.noInstanceConfigWarningModal === true || !serverConfig.signup.allowed) return | 242 | } |
239 | 243 | ||
240 | this.instanceService.getAbout() | 244 | if (!this.instanceConfigWarningModal.shouldOpenByUser(user)) return |
241 | .subscribe(about => { | 245 | |
242 | if ( | 246 | forkJoin([ |
243 | this.serverConfig.instance.name.toLowerCase() === 'peertube' || | 247 | this.serverService.getConfig().pipe(first()), |
244 | !about.instance.terms || | 248 | this.instanceService.getAbout().pipe(first()) |
245 | !about.instance.administrator || | 249 | ]).subscribe(([ config, about ]) => { |
246 | !about.instance.maintenanceLifetime | 250 | if (this.instanceConfigWarningModal.shouldOpen(config, about)) { |
247 | ) { | 251 | this.instanceConfigWarningModal.show(about) |
248 | this.instanceConfigWarningModal.show(about) | 252 | } |
249 | } | 253 | }) |
250 | }) | 254 | } |
255 | |||
256 | private openAccountModalsIfNeeded (user: User) { | ||
257 | if (this.accountSetupWarningModal.shouldOpen(user)) { | ||
258 | this.accountSetupWarningModal.show(user) | ||
259 | } | ||
251 | } | 260 | } |
252 | 261 | ||
253 | private initHotkeys () { | 262 | private initHotkeys () { |
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 8d9c39ad8..bb20c2d83 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts | |||
@@ -17,8 +17,8 @@ import { ConfirmComponent } from './modal/confirm.component' | |||
17 | import { CustomModalComponent } from './modal/custom-modal.component' | 17 | import { CustomModalComponent } from './modal/custom-modal.component' |
18 | import { InstanceConfigWarningModalComponent } from './modal/instance-config-warning-modal.component' | 18 | import { InstanceConfigWarningModalComponent } from './modal/instance-config-warning-modal.component' |
19 | import { QuickSettingsModalComponent } from './modal/quick-settings-modal.component' | 19 | import { QuickSettingsModalComponent } from './modal/quick-settings-modal.component' |
20 | import { WelcomeModalComponent } from './modal/welcome-modal.component' | 20 | import { AdminWelcomeModalComponent } from './modal/admin-welcome-modal.component' |
21 | import { AccountSetupModalComponent } from './modal/account-setup-modal.component' | 21 | import { AccountSetupWarningModalComponent } from './modal/account-setup-warning-modal.component' |
22 | import { SharedActorImageModule } from './shared/shared-actor-image/shared-actor-image.module' | 22 | import { SharedActorImageModule } from './shared/shared-actor-image/shared-actor-image.module' |
23 | import { SharedFormModule } from './shared/shared-forms' | 23 | import { SharedFormModule } from './shared/shared-forms' |
24 | import { SharedGlobalIconModule } from './shared/shared-icons' | 24 | import { SharedGlobalIconModule } from './shared/shared-icons' |
@@ -54,9 +54,9 @@ export function loadConfigFactory (server: ServerService, pluginService: PluginS | |||
54 | SuggestionComponent, | 54 | SuggestionComponent, |
55 | HighlightPipe, | 55 | HighlightPipe, |
56 | 56 | ||
57 | AccountSetupModalComponent, | 57 | AccountSetupWarningModalComponent, |
58 | CustomModalComponent, | 58 | CustomModalComponent, |
59 | WelcomeModalComponent, | 59 | AdminWelcomeModalComponent, |
60 | InstanceConfigWarningModalComponent, | 60 | InstanceConfigWarningModalComponent, |
61 | ConfirmComponent | 61 | ConfirmComponent |
62 | ], | 62 | ], |
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts index 7467519c4..00078af5d 100644 --- a/client/src/app/core/users/user.model.ts +++ b/client/src/app/core/users/user.model.ts | |||
@@ -55,6 +55,7 @@ export class User implements UserServerModel { | |||
55 | 55 | ||
56 | noInstanceConfigWarningModal: boolean | 56 | noInstanceConfigWarningModal: boolean |
57 | noWelcomeModal: boolean | 57 | noWelcomeModal: boolean |
58 | noAccountSetupWarningModal: boolean | ||
58 | 59 | ||
59 | pluginAuth: string | null | 60 | pluginAuth: string | null |
60 | 61 | ||
@@ -98,6 +99,7 @@ export class User implements UserServerModel { | |||
98 | 99 | ||
99 | this.noInstanceConfigWarningModal = hash.noInstanceConfigWarningModal | 100 | this.noInstanceConfigWarningModal = hash.noInstanceConfigWarningModal |
100 | this.noWelcomeModal = hash.noWelcomeModal | 101 | this.noWelcomeModal = hash.noWelcomeModal |
102 | this.noAccountSetupWarningModal = hash.noAccountSetupWarningModal | ||
101 | 103 | ||
102 | this.notificationSettings = hash.notificationSettings | 104 | this.notificationSettings = hash.notificationSettings |
103 | 105 | ||
diff --git a/client/src/app/modal/account-setup-modal.component.ts b/client/src/app/modal/account-setup-modal.component.ts deleted file mode 100644 index 4cb8de2c7..000000000 --- a/client/src/app/modal/account-setup-modal.component.ts +++ /dev/null | |||
@@ -1,73 +0,0 @@ | |||
1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' | ||
2 | import { AuthService, ServerService, User, UserService } from '@app/core' | ||
3 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' | ||
4 | import { HTMLServerConfig } from '@shared/models' | ||
5 | |||
6 | @Component({ | ||
7 | selector: 'my-account-setup-modal', | ||
8 | templateUrl: './account-setup-modal.component.html', | ||
9 | styleUrls: [ './account-setup-modal.component.scss' ] | ||
10 | }) | ||
11 | export class AccountSetupModalComponent implements OnInit { | ||
12 | @ViewChild('modal', { static: true }) modal: ElementRef | ||
13 | |||
14 | user: User = null | ||
15 | ref: NgbModalRef = null | ||
16 | |||
17 | private serverConfig: HTMLServerConfig | ||
18 | |||
19 | constructor ( | ||
20 | private userService: UserService, | ||
21 | private authService: AuthService, | ||
22 | private modalService: NgbModal, | ||
23 | private serverService: ServerService | ||
24 | ) { } | ||
25 | |||
26 | get userInformationLoaded () { | ||
27 | return this.authService.userInformationLoaded | ||
28 | } | ||
29 | |||
30 | get instanceName () { | ||
31 | return this.serverConfig.instance.name | ||
32 | } | ||
33 | |||
34 | get isUserRoot () { | ||
35 | return this.user.username === 'root' | ||
36 | } | ||
37 | |||
38 | get hasAccountAvatar () { | ||
39 | return !!this.user.account.avatar | ||
40 | } | ||
41 | |||
42 | get hasAccountDescription () { | ||
43 | return !!this.user.account.description | ||
44 | } | ||
45 | |||
46 | ngOnInit () { | ||
47 | this.serverConfig = this.serverService.getHTMLConfig() | ||
48 | |||
49 | this.authService.userInformationLoaded | ||
50 | .subscribe( | ||
51 | () => { | ||
52 | this.user = this.authService.getUser() | ||
53 | |||
54 | if (this.isUserRoot) return | ||
55 | if (this.hasAccountAvatar && this.hasAccountDescription) return | ||
56 | if (this.userService.hasSignupInThisSession()) return | ||
57 | |||
58 | this.show() | ||
59 | } | ||
60 | ) | ||
61 | } | ||
62 | |||
63 | show () { | ||
64 | if (this.ref) return | ||
65 | |||
66 | this.ref = this.modalService.open(this.modal, { | ||
67 | centered: true, | ||
68 | backdrop: 'static', | ||
69 | keyboard: false, | ||
70 | size: 'md' | ||
71 | }) | ||
72 | } | ||
73 | } | ||
diff --git a/client/src/app/modal/account-setup-modal.component.html b/client/src/app/modal/account-setup-warning-modal.component.html index d1f153835..614c0693d 100644 --- a/client/src/app/modal/account-setup-modal.component.html +++ b/client/src/app/modal/account-setup-warning-modal.component.html | |||
@@ -12,12 +12,18 @@ | |||
12 | <p i18n>Help moderators and other users to know <strong>who you are</strong> by:</p> | 12 | <p i18n>Help moderators and other users to know <strong>who you are</strong> by:</p> |
13 | 13 | ||
14 | <ul> | 14 | <ul> |
15 | <li *ngIf="!hasAccountAvatar" i18n>Uploading an <strong>avatar</strong></li> | 15 | <li *ngIf="!hasAccountAvatar(user)" i18n>Uploading an <strong>avatar</strong></li> |
16 | <li *ngIf="!hasAccountDescription" i18n>Writing a <strong>description</strong></li> | 16 | <li *ngIf="!hasAccountDescription(user)" i18n>Writing a <strong>description</strong></li> |
17 | </ul> | 17 | </ul> |
18 | </div> | 18 | </div> |
19 | 19 | ||
20 | <div class="modal-footer inputs"> | 20 | <div class="modal-footer inputs"> |
21 | <my-peertube-checkbox | ||
22 | inputName="stopDisplayModal" [(ngModel)]="stopDisplayModal" | ||
23 | i18n-labelText labelText="Don't show me this anymore" | ||
24 | > | ||
25 | </my-peertube-checkbox> | ||
26 | |||
21 | <input | 27 | <input |
22 | type="button" role="button" i18n-value value="Remind me later" class="peertube-button grey-button" | 28 | type="button" role="button" i18n-value value="Remind me later" class="peertube-button grey-button" |
23 | (click)="hide()" (key.enter)="hide()" | 29 | (click)="hide()" (key.enter)="hide()" |
diff --git a/client/src/app/modal/account-setup-modal.component.scss b/client/src/app/modal/account-setup-warning-modal.component.scss index d99edaf7a..d99edaf7a 100644 --- a/client/src/app/modal/account-setup-modal.component.scss +++ b/client/src/app/modal/account-setup-warning-modal.component.scss | |||
diff --git a/client/src/app/modal/account-setup-warning-modal.component.ts b/client/src/app/modal/account-setup-warning-modal.component.ts new file mode 100644 index 000000000..c78de447d --- /dev/null +++ b/client/src/app/modal/account-setup-warning-modal.component.ts | |||
@@ -0,0 +1,79 @@ | |||
1 | import { Component, ElementRef, ViewChild } from '@angular/core' | ||
2 | import { Notifier, ServerService, User, UserService } from '@app/core' | ||
3 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' | ||
4 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | ||
5 | |||
6 | @Component({ | ||
7 | selector: 'my-account-setup-warning-modal', | ||
8 | templateUrl: './account-setup-warning-modal.component.html', | ||
9 | styleUrls: [ './account-setup-warning-modal.component.scss' ] | ||
10 | }) | ||
11 | export class AccountSetupWarningModalComponent { | ||
12 | @ViewChild('modal', { static: true }) modal: ElementRef | ||
13 | |||
14 | stopDisplayModal = false | ||
15 | ref: NgbModalRef | ||
16 | |||
17 | user: User | ||
18 | |||
19 | private LOCAL_STORAGE_KEYS = { | ||
20 | NO_ACCOUNT_SETUP_WARNING_MODAL: 'no_account_setup_warning_modal' | ||
21 | } | ||
22 | |||
23 | constructor ( | ||
24 | private userService: UserService, | ||
25 | private modalService: NgbModal, | ||
26 | private notifier: Notifier, | ||
27 | private serverService: ServerService | ||
28 | ) { } | ||
29 | |||
30 | get instanceName () { | ||
31 | return this.serverService.getHTMLConfig().instance.name | ||
32 | } | ||
33 | |||
34 | hasAccountAvatar (user: User) { | ||
35 | return !!user.account.avatar | ||
36 | } | ||
37 | |||
38 | hasAccountDescription (user: User) { | ||
39 | return !!user.account.description | ||
40 | } | ||
41 | |||
42 | shouldOpen (user: User) { | ||
43 | if (user.noAccountSetupWarningModal === true) return false | ||
44 | if (peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_ACCOUNT_SETUP_WARNING_MODAL) === 'true') return false | ||
45 | |||
46 | if (this.hasAccountAvatar(user) && this.hasAccountDescription(user)) return false | ||
47 | if (this.userService.hasSignupInThisSession()) return false | ||
48 | |||
49 | return true | ||
50 | } | ||
51 | |||
52 | show (user: User) { | ||
53 | this.user = user | ||
54 | |||
55 | if (this.ref) return | ||
56 | |||
57 | this.ref = this.modalService.open(this.modal, { | ||
58 | centered: true, | ||
59 | backdrop: 'static', | ||
60 | keyboard: false, | ||
61 | size: 'md' | ||
62 | }) | ||
63 | |||
64 | this.ref.result.finally(() => { | ||
65 | if (this.stopDisplayModal === true) this.doNotOpenAgain() | ||
66 | }) | ||
67 | } | ||
68 | |||
69 | private doNotOpenAgain () { | ||
70 | peertubeLocalStorage.setItem(this.LOCAL_STORAGE_KEYS.NO_ACCOUNT_SETUP_WARNING_MODAL, 'true') | ||
71 | |||
72 | this.userService.updateMyProfile({ noAccountSetupWarningModal: true }) | ||
73 | .subscribe({ | ||
74 | next: () => console.log('We will not open the account setup modal again.'), | ||
75 | |||
76 | error: err => this.notifier.error(err.message) | ||
77 | }) | ||
78 | } | ||
79 | } | ||
diff --git a/client/src/app/modal/welcome-modal.component.html b/client/src/app/modal/admin-welcome-modal.component.html index f5d2b8799..f5d2b8799 100644 --- a/client/src/app/modal/welcome-modal.component.html +++ b/client/src/app/modal/admin-welcome-modal.component.html | |||
diff --git a/client/src/app/modal/welcome-modal.component.scss b/client/src/app/modal/admin-welcome-modal.component.scss index 242a498d0..242a498d0 100644 --- a/client/src/app/modal/welcome-modal.component.scss +++ b/client/src/app/modal/admin-welcome-modal.component.scss | |||
diff --git a/client/src/app/modal/welcome-modal.component.ts b/client/src/app/modal/admin-welcome-modal.component.ts index 06fe4cba5..3679f0847 100644 --- a/client/src/app/modal/welcome-modal.component.ts +++ b/client/src/app/modal/admin-welcome-modal.component.ts | |||
@@ -1,14 +1,14 @@ | |||
1 | import { Component, ElementRef, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, ViewChild } from '@angular/core' |
2 | import { Notifier, UserService } from '@app/core' | 2 | import { Notifier, User, UserService } from '@app/core' |
3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
4 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | 4 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
5 | 5 | ||
6 | @Component({ | 6 | @Component({ |
7 | selector: 'my-welcome-modal', | 7 | selector: 'my-admin-welcome-modal', |
8 | templateUrl: './welcome-modal.component.html', | 8 | templateUrl: './admin-welcome-modal.component.html', |
9 | styleUrls: [ './welcome-modal.component.scss' ] | 9 | styleUrls: [ './admin-welcome-modal.component.scss' ] |
10 | }) | 10 | }) |
11 | export class WelcomeModalComponent { | 11 | export class AdminWelcomeModalComponent { |
12 | @ViewChild('modal', { static: true }) modal: ElementRef | 12 | @ViewChild('modal', { static: true }) modal: ElementRef |
13 | 13 | ||
14 | private LOCAL_STORAGE_KEYS = { | 14 | private LOCAL_STORAGE_KEYS = { |
@@ -21,10 +21,14 @@ export class WelcomeModalComponent { | |||
21 | private notifier: Notifier | 21 | private notifier: Notifier |
22 | ) { } | 22 | ) { } |
23 | 23 | ||
24 | show () { | 24 | shouldOpen (user: User) { |
25 | const result = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_WELCOME_MODAL) | 25 | if (user.noWelcomeModal === true) return false |
26 | if (result === 'true') return | 26 | if (peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_WELCOME_MODAL) === 'true') return false |
27 | |||
28 | return true | ||
29 | } | ||
27 | 30 | ||
31 | show () { | ||
28 | this.modalService.open(this.modal, { | 32 | this.modalService.open(this.modal, { |
29 | centered: true, | 33 | centered: true, |
30 | backdrop: 'static', | 34 | backdrop: 'static', |
diff --git a/client/src/app/modal/instance-config-warning-modal.component.ts b/client/src/app/modal/instance-config-warning-modal.component.ts index e40958976..300738a41 100644 --- a/client/src/app/modal/instance-config-warning-modal.component.ts +++ b/client/src/app/modal/instance-config-warning-modal.component.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import { Location } from '@angular/common' | 1 | import { Location } from '@angular/common' |
2 | import { Component, ElementRef, ViewChild } from '@angular/core' | 2 | import { Component, ElementRef, ViewChild } from '@angular/core' |
3 | import { Notifier, UserService } from '@app/core' | 3 | import { Notifier, User, UserService } from '@app/core' |
4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
5 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | 5 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
6 | import { About } from '@shared/models/server' | 6 | import { About, ServerConfig } from '@shared/models/server' |
7 | 7 | ||
8 | @Component({ | 8 | @Component({ |
9 | selector: 'my-instance-config-warning-modal', | 9 | selector: 'my-instance-config-warning-modal', |
@@ -27,10 +27,23 @@ export class InstanceConfigWarningModalComponent { | |||
27 | private notifier: Notifier | 27 | private notifier: Notifier |
28 | ) { } | 28 | ) { } |
29 | 29 | ||
30 | show (about: About) { | 30 | shouldOpenByUser (user: User) { |
31 | const result = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_INSTANCE_CONFIG_WARNING_MODAL) | 31 | if (user.noInstanceConfigWarningModal === true) return false |
32 | if (result === 'true') return | 32 | if (peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_INSTANCE_CONFIG_WARNING_MODAL) === 'true') return false |
33 | |||
34 | return true | ||
35 | } | ||
36 | |||
37 | shouldOpen (serverConfig: ServerConfig, about: About) { | ||
38 | if (!serverConfig.signup.allowed) return false | ||
33 | 39 | ||
40 | return serverConfig.instance.name.toLowerCase() === 'peertube' || | ||
41 | !about.instance.terms || | ||
42 | !about.instance.administrator || | ||
43 | !about.instance.maintenanceLifetime | ||
44 | } | ||
45 | |||
46 | show (about: About) { | ||
34 | if (this.location.path().startsWith('/admin/config/edit-custom')) return | 47 | if (this.location.path().startsWith('/admin/config/edit-custom')) return |
35 | 48 | ||
36 | this.about = about | 49 | this.about = about |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index ac6faca9c..a98d79218 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -203,6 +203,7 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
203 | 'videoLanguages', | 203 | 'videoLanguages', |
204 | 'theme', | 204 | 'theme', |
205 | 'noInstanceConfigWarningModal', | 205 | 'noInstanceConfigWarningModal', |
206 | 'noAccountSetupWarningModal', | ||
206 | 'noWelcomeModal' | 207 | 'noWelcomeModal' |
207 | ] | 208 | ] |
208 | 209 | ||
diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts index 5b21c3529..f52c60b60 100644 --- a/server/helpers/custom-validators/users.ts +++ b/server/helpers/custom-validators/users.ts | |||
@@ -81,11 +81,7 @@ function isUserAutoPlayNextVideoPlaylistValid (value: any) { | |||
81 | return isBooleanValid(value) | 81 | return isBooleanValid(value) |
82 | } | 82 | } |
83 | 83 | ||
84 | function isNoInstanceConfigWarningModal (value: any) { | 84 | function isUserNoModal (value: any) { |
85 | return isBooleanValid(value) | ||
86 | } | ||
87 | |||
88 | function isNoWelcomeModal (value: any) { | ||
89 | return isBooleanValid(value) | 85 | return isBooleanValid(value) |
90 | } | 86 | } |
91 | 87 | ||
@@ -119,6 +115,5 @@ export { | |||
119 | isUserAutoPlayNextVideoPlaylistValid, | 115 | isUserAutoPlayNextVideoPlaylistValid, |
120 | isUserDisplayNameValid, | 116 | isUserDisplayNameValid, |
121 | isUserDescriptionValid, | 117 | isUserDescriptionValid, |
122 | isNoInstanceConfigWarningModal, | 118 | isUserNoModal |
123 | isNoWelcomeModal | ||
124 | } | 119 | } |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 8a1526ae8..2d8087f46 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
24 | 24 | ||
25 | // --------------------------------------------------------------------------- | 25 | // --------------------------------------------------------------------------- |
26 | 26 | ||
27 | const LAST_MIGRATION_VERSION = 660 | 27 | const LAST_MIGRATION_VERSION = 665 |
28 | 28 | ||
29 | // --------------------------------------------------------------------------- | 29 | // --------------------------------------------------------------------------- |
30 | 30 | ||
diff --git a/server/initializers/migrations/0665-no-account-warning-modal.ts b/server/initializers/migrations/0665-no-account-warning-modal.ts new file mode 100644 index 000000000..6718aed85 --- /dev/null +++ b/server/initializers/migrations/0665-no-account-warning-modal.ts | |||
@@ -0,0 +1,27 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction | ||
5 | queryInterface: Sequelize.QueryInterface | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | const data = { | ||
11 | type: Sequelize.BOOLEAN, | ||
12 | allowNull: false, | ||
13 | defaultValue: false | ||
14 | } | ||
15 | |||
16 | await utils.queryInterface.addColumn('user', 'noAccountSetupWarningModal', data) | ||
17 | } | ||
18 | } | ||
19 | |||
20 | function down (options) { | ||
21 | throw new Error('Not implemented.') | ||
22 | } | ||
23 | |||
24 | export { | ||
25 | up, | ||
26 | down | ||
27 | } | ||
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index bc8607523..c6977fcd9 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -9,14 +9,13 @@ import { UserRegister } from '../../../shared/models/users/user-register.model' | |||
9 | import { toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' | 9 | import { toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' |
10 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' | 10 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' |
11 | import { | 11 | import { |
12 | isNoInstanceConfigWarningModal, | ||
13 | isNoWelcomeModal, | ||
14 | isUserAdminFlagsValid, | 12 | isUserAdminFlagsValid, |
15 | isUserAutoPlayNextVideoValid, | 13 | isUserAutoPlayNextVideoValid, |
16 | isUserAutoPlayVideoValid, | 14 | isUserAutoPlayVideoValid, |
17 | isUserBlockedReasonValid, | 15 | isUserBlockedReasonValid, |
18 | isUserDescriptionValid, | 16 | isUserDescriptionValid, |
19 | isUserDisplayNameValid, | 17 | isUserDisplayNameValid, |
18 | isUserNoModal, | ||
20 | isUserNSFWPolicyValid, | 19 | isUserNSFWPolicyValid, |
21 | isUserPasswordValid, | 20 | isUserPasswordValid, |
22 | isUserPasswordValidOrEmpty, | 21 | isUserPasswordValidOrEmpty, |
@@ -251,12 +250,17 @@ const usersUpdateMeValidator = [ | |||
251 | body('theme') | 250 | body('theme') |
252 | .optional() | 251 | .optional() |
253 | .custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'), | 252 | .custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'), |
253 | |||
254 | body('noInstanceConfigWarningModal') | 254 | body('noInstanceConfigWarningModal') |
255 | .optional() | 255 | .optional() |
256 | .custom(v => isNoInstanceConfigWarningModal(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'), | 256 | .custom(v => isUserNoModal(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'), |
257 | body('noWelcomeModal') | 257 | body('noWelcomeModal') |
258 | .optional() | 258 | .optional() |
259 | .custom(v => isNoWelcomeModal(v)).withMessage('Should have a valid noWelcomeModal boolean'), | 259 | .custom(v => isUserNoModal(v)).withMessage('Should have a valid noWelcomeModal boolean'), |
260 | body('noAccountSetupWarningModal') | ||
261 | .optional() | ||
262 | .custom(v => isUserNoModal(v)).withMessage('Should have a valid noAccountSetupWarningModal boolean'), | ||
263 | |||
260 | body('autoPlayNextVideo') | 264 | body('autoPlayNextVideo') |
261 | .optional() | 265 | .optional() |
262 | .custom(v => isUserAutoPlayNextVideoValid(v)).withMessage('Should have a valid autoPlayNextVideo boolean'), | 266 | .custom(v => isUserAutoPlayNextVideoValid(v)).withMessage('Should have a valid autoPlayNextVideo boolean'), |
diff --git a/server/models/user/user.ts b/server/models/user/user.ts index 069d7266e..ddce455a1 100644 --- a/server/models/user/user.ts +++ b/server/models/user/user.ts | |||
@@ -39,8 +39,6 @@ import { UserAdminFlag } from '../../../shared/models/users/user-flag.model' | |||
39 | import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' | 39 | import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' |
40 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' | 40 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' |
41 | import { | 41 | import { |
42 | isNoInstanceConfigWarningModal, | ||
43 | isNoWelcomeModal, | ||
44 | isUserAdminFlagsValid, | 42 | isUserAdminFlagsValid, |
45 | isUserAutoPlayNextVideoPlaylistValid, | 43 | isUserAutoPlayNextVideoPlaylistValid, |
46 | isUserAutoPlayNextVideoValid, | 44 | isUserAutoPlayNextVideoValid, |
@@ -48,6 +46,7 @@ import { | |||
48 | isUserBlockedReasonValid, | 46 | isUserBlockedReasonValid, |
49 | isUserBlockedValid, | 47 | isUserBlockedValid, |
50 | isUserEmailVerifiedValid, | 48 | isUserEmailVerifiedValid, |
49 | isUserNoModal, | ||
51 | isUserNSFWPolicyValid, | 50 | isUserNSFWPolicyValid, |
52 | isUserPasswordValid, | 51 | isUserPasswordValid, |
53 | isUserRoleValid, | 52 | isUserRoleValid, |
@@ -349,7 +348,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> { | |||
349 | @Default(false) | 348 | @Default(false) |
350 | @Is( | 349 | @Is( |
351 | 'UserNoInstanceConfigWarningModal', | 350 | 'UserNoInstanceConfigWarningModal', |
352 | value => throwIfNotValid(value, isNoInstanceConfigWarningModal, 'no instance config warning modal') | 351 | value => throwIfNotValid(value, isUserNoModal, 'no instance config warning modal') |
353 | ) | 352 | ) |
354 | @Column | 353 | @Column |
355 | noInstanceConfigWarningModal: boolean | 354 | noInstanceConfigWarningModal: boolean |
@@ -357,12 +356,21 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> { | |||
357 | @AllowNull(false) | 356 | @AllowNull(false) |
358 | @Default(false) | 357 | @Default(false) |
359 | @Is( | 358 | @Is( |
360 | 'UserNoInstanceConfigWarningModal', | 359 | 'UserNoWelcomeModal', |
361 | value => throwIfNotValid(value, isNoWelcomeModal, 'no welcome modal') | 360 | value => throwIfNotValid(value, isUserNoModal, 'no welcome modal') |
362 | ) | 361 | ) |
363 | @Column | 362 | @Column |
364 | noWelcomeModal: boolean | 363 | noWelcomeModal: boolean |
365 | 364 | ||
365 | @AllowNull(false) | ||
366 | @Default(false) | ||
367 | @Is( | ||
368 | 'UserNoAccountSetupWarningModal', | ||
369 | value => throwIfNotValid(value, isUserNoModal, 'no account setup warning modal') | ||
370 | ) | ||
371 | @Column | ||
372 | noAccountSetupWarningModal: boolean | ||
373 | |||
366 | @AllowNull(true) | 374 | @AllowNull(true) |
367 | @Default(null) | 375 | @Default(null) |
368 | @Column | 376 | @Column |
@@ -920,6 +928,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> { | |||
920 | 928 | ||
921 | noInstanceConfigWarningModal: this.noInstanceConfigWarningModal, | 929 | noInstanceConfigWarningModal: this.noInstanceConfigWarningModal, |
922 | noWelcomeModal: this.noWelcomeModal, | 930 | noWelcomeModal: this.noWelcomeModal, |
931 | noAccountSetupWarningModal: this.noAccountSetupWarningModal, | ||
923 | 932 | ||
924 | blocked: this.blocked, | 933 | blocked: this.blocked, |
925 | blockedReason: this.blockedReason, | 934 | blockedReason: this.blockedReason, |
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts index 9d8f933db..58b360f92 100644 --- a/server/tests/api/check-params/users.ts +++ b/server/tests/api/check-params/users.ts | |||
@@ -491,20 +491,20 @@ describe('Test users API validators', function () { | |||
491 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields }) | 491 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields }) |
492 | }) | 492 | }) |
493 | 493 | ||
494 | it('Should fail with an invalid noInstanceConfigWarningModal attribute', async function () { | 494 | it('Should fail with invalid no modal attributes', async function () { |
495 | const fields = { | 495 | const keys = [ |
496 | noInstanceConfigWarningModal: -1 | 496 | 'noInstanceConfigWarningModal', |
497 | } | 497 | 'noAccountSetupWarningModal', |
498 | 498 | 'noWelcomeModal' | |
499 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields }) | 499 | ] |
500 | }) | 500 | |
501 | for (const key of keys) { | ||
502 | const fields = { | ||
503 | [key]: -1 | ||
504 | } | ||
501 | 505 | ||
502 | it('Should fail with an invalid noWelcomeModal attribute', async function () { | 506 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields }) |
503 | const fields = { | ||
504 | noWelcomeModal: -1 | ||
505 | } | 507 | } |
506 | |||
507 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields }) | ||
508 | }) | 508 | }) |
509 | 509 | ||
510 | it('Should succeed to change password with the correct params', async function () { | 510 | it('Should succeed to change password with the correct params', async function () { |
@@ -516,7 +516,8 @@ describe('Test users API validators', function () { | |||
516 | email: 'super_email@example.com', | 516 | email: 'super_email@example.com', |
517 | theme: 'default', | 517 | theme: 'default', |
518 | noInstanceConfigWarningModal: true, | 518 | noInstanceConfigWarningModal: true, |
519 | noWelcomeModal: true | 519 | noWelcomeModal: true, |
520 | noAccountSetupWarningModal: true | ||
520 | } | 521 | } |
521 | 522 | ||
522 | await makePutBodyRequest({ | 523 | await makePutBodyRequest({ |
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index 318ff832a..085d9d870 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts | |||
@@ -597,6 +597,7 @@ describe('Test users', function () { | |||
597 | expect(user.account.description).to.equal('my super description updated') | 597 | expect(user.account.description).to.equal('my super description updated') |
598 | expect(user.noWelcomeModal).to.be.false | 598 | expect(user.noWelcomeModal).to.be.false |
599 | expect(user.noInstanceConfigWarningModal).to.be.false | 599 | expect(user.noInstanceConfigWarningModal).to.be.false |
600 | expect(user.noAccountSetupWarningModal).to.be.false | ||
600 | }) | 601 | }) |
601 | 602 | ||
602 | it('Should be able to update my theme', async function () { | 603 | it('Should be able to update my theme', async function () { |
@@ -612,12 +613,14 @@ describe('Test users', function () { | |||
612 | await server.users.updateMe({ | 613 | await server.users.updateMe({ |
613 | token: userToken, | 614 | token: userToken, |
614 | noInstanceConfigWarningModal: true, | 615 | noInstanceConfigWarningModal: true, |
615 | noWelcomeModal: true | 616 | noWelcomeModal: true, |
617 | noAccountSetupWarningModal: true | ||
616 | }) | 618 | }) |
617 | 619 | ||
618 | const user = await server.users.getMyInfo({ token: userToken }) | 620 | const user = await server.users.getMyInfo({ token: userToken }) |
619 | expect(user.noWelcomeModal).to.be.true | 621 | expect(user.noWelcomeModal).to.be.true |
620 | expect(user.noInstanceConfigWarningModal).to.be.true | 622 | expect(user.noInstanceConfigWarningModal).to.be.true |
623 | expect(user.noAccountSetupWarningModal).to.be.true | ||
621 | }) | 624 | }) |
622 | }) | 625 | }) |
623 | 626 | ||
diff --git a/shared/models/users/user-update-me.model.ts b/shared/models/users/user-update-me.model.ts index db6539cd2..6d7df38fb 100644 --- a/shared/models/users/user-update-me.model.ts +++ b/shared/models/users/user-update-me.model.ts | |||
@@ -20,4 +20,5 @@ export interface UserUpdateMe { | |||
20 | 20 | ||
21 | noInstanceConfigWarningModal?: boolean | 21 | noInstanceConfigWarningModal?: boolean |
22 | noWelcomeModal?: boolean | 22 | noWelcomeModal?: boolean |
23 | noAccountSetupWarningModal?: boolean | ||
23 | } | 24 | } |
diff --git a/shared/models/users/user.model.ts b/shared/models/users/user.model.ts index 7a982c2e5..78870c556 100644 --- a/shared/models/users/user.model.ts +++ b/shared/models/users/user.model.ts | |||
@@ -51,6 +51,7 @@ export interface User { | |||
51 | 51 | ||
52 | noInstanceConfigWarningModal: boolean | 52 | noInstanceConfigWarningModal: boolean |
53 | noWelcomeModal: boolean | 53 | noWelcomeModal: boolean |
54 | noAccountSetupWarningModal: boolean | ||
54 | 55 | ||
55 | createdAt: Date | 56 | createdAt: Date |
56 | 57 | ||
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 76e78fe53..65a2ac897 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml | |||
@@ -6376,6 +6376,8 @@ components: | |||
6376 | format: date-time | 6376 | format: date-time |
6377 | noInstanceConfigWarningModal: | 6377 | noInstanceConfigWarningModal: |
6378 | type: boolean | 6378 | type: boolean |
6379 | noAccountSetupWarningModal: | ||
6380 | type: boolean | ||
6379 | noWelcomeModal: | 6381 | noWelcomeModal: |
6380 | type: boolean | 6382 | type: boolean |
6381 | nsfwPolicy: | 6383 | nsfwPolicy: |
@@ -6530,6 +6532,8 @@ components: | |||
6530 | type: string | 6532 | type: string |
6531 | noInstanceConfigWarningModal: | 6533 | noInstanceConfigWarningModal: |
6532 | type: boolean | 6534 | type: boolean |
6535 | noAccountSetupWarningModal: | ||
6536 | type: boolean | ||
6533 | noWelcomeModal: | 6537 | noWelcomeModal: |
6534 | type: boolean | 6538 | type: boolean |
6535 | GetMeVideoRating: | 6539 | GetMeVideoRating: |