</p-toast>
<ng-container *ngIf="isUserLoggedIn()">
- <my-account-setup-modal #accountSetupModal></my-account-setup-modal>
- <my-welcome-modal #welcomeModal></my-welcome-modal>
+ <my-account-setup-warning-modal #accountSetupWarningModal></my-account-setup-warning-modal>
+ <my-admin-welcome-modal #adminWelcomeModal></my-admin-welcome-modal>
<my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal>
</ng-container>
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
-import { filter, map, switchMap } from 'rxjs/operators'
+import { forkJoin } from 'rxjs'
+import { filter, first, map } from 'rxjs/operators'
import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common'
import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
} from '@app/core'
import { HooksService } from '@app/core/plugins/hooks.service'
import { PluginService } from '@app/core/plugins/plugin.service'
+import { AccountSetupWarningModalComponent } from '@app/modal/account-setup-warning-modal.component'
import { CustomModalComponent } from '@app/modal/custom-modal.component'
import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component'
-import { WelcomeModalComponent } from '@app/modal/welcome-modal.component'
-import { AccountSetupModalComponent } from '@app/modal/account-setup-modal.component'
+import { AdminWelcomeModalComponent } from '@app/modal/admin-welcome-modal.component'
import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { LoadingBarService } from '@ngx-loading-bar/core'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { getShortLocale } from '@shared/core-utils/i18n'
-import { BroadcastMessageLevel, HTMLServerConfig, ServerConfig, UserRole } from '@shared/models'
+import { BroadcastMessageLevel, HTMLServerConfig, UserRole } from '@shared/models'
import { MenuService } from './core/menu/menu.service'
import { POP_STATE_MODAL_DISMISS } from './helpers'
import { InstanceService } from './shared/shared-instance'
export class AppComponent implements OnInit, AfterViewInit {
private static BROADCAST_MESSAGE_KEY = 'app-broadcast-message-dismissed'
- @ViewChild('accountSetupModal') accountSetupModal: AccountSetupModalComponent
- @ViewChild('welcomeModal') welcomeModal: WelcomeModalComponent
+ @ViewChild('accountSetupWarningModal') accountSetupWarningModal: AccountSetupWarningModalComponent
+ @ViewChild('adminWelcomeModal') adminWelcomeModal: AdminWelcomeModalComponent
@ViewChild('instanceConfigWarningModal') instanceConfigWarningModal: InstanceConfigWarningModalComponent
@ViewChild('customModal') customModal: CustomModalComponent
}
private openModalsIfNeeded () {
- this.authService.userInformationLoaded
- .pipe(
- map(() => this.authService.getUser()),
- filter(user => user.role === UserRole.ADMINISTRATOR),
- switchMap(user => {
- return this.serverService.getConfig()
- .pipe(map(serverConfig => ({ serverConfig, user })))
- })
- ).subscribe(({ serverConfig, user }) => this._openAdminModalsIfNeeded(serverConfig, user))
+ const userSub = this.authService.userInformationLoaded
+ .pipe(map(() => this.authService.getUser()))
+
+ // Admin modal
+ userSub.pipe(
+ filter(user => user.role === UserRole.ADMINISTRATOR)
+ ).subscribe(user => this.openAdminModalsIfNeeded(user))
+
+ // Account modal
+ userSub.pipe(
+ filter(user => user.role !== UserRole.ADMINISTRATOR)
+ ).subscribe(user => this.openAccountModalsIfNeeded(user))
}
- private _openAdminModalsIfNeeded (serverConfig: ServerConfig, user: User) {
- if (user.noWelcomeModal !== true) return this.welcomeModal.show()
-
- if (user.noInstanceConfigWarningModal === true || !serverConfig.signup.allowed) return
-
- this.instanceService.getAbout()
- .subscribe(about => {
- if (
- this.serverConfig.instance.name.toLowerCase() === 'peertube' ||
- !about.instance.terms ||
- !about.instance.administrator ||
- !about.instance.maintenanceLifetime
- ) {
- this.instanceConfigWarningModal.show(about)
- }
- })
+ private openAdminModalsIfNeeded (user: User) {
+ if (this.adminWelcomeModal.shouldOpen(user)) {
+ return this.adminWelcomeModal.show()
+ }
+
+ if (!this.instanceConfigWarningModal.shouldOpenByUser(user)) return
+
+ forkJoin([
+ this.serverService.getConfig().pipe(first()),
+ this.instanceService.getAbout().pipe(first())
+ ]).subscribe(([ config, about ]) => {
+ if (this.instanceConfigWarningModal.shouldOpen(config, about)) {
+ this.instanceConfigWarningModal.show(about)
+ }
+ })
+ }
+
+ private openAccountModalsIfNeeded (user: User) {
+ if (this.accountSetupWarningModal.shouldOpen(user)) {
+ this.accountSetupWarningModal.show(user)
+ }
}
private initHotkeys () {
import { CustomModalComponent } from './modal/custom-modal.component'
import { InstanceConfigWarningModalComponent } from './modal/instance-config-warning-modal.component'
import { QuickSettingsModalComponent } from './modal/quick-settings-modal.component'
-import { WelcomeModalComponent } from './modal/welcome-modal.component'
-import { AccountSetupModalComponent } from './modal/account-setup-modal.component'
+import { AdminWelcomeModalComponent } from './modal/admin-welcome-modal.component'
+import { AccountSetupWarningModalComponent } from './modal/account-setup-warning-modal.component'
import { SharedActorImageModule } from './shared/shared-actor-image/shared-actor-image.module'
import { SharedFormModule } from './shared/shared-forms'
import { SharedGlobalIconModule } from './shared/shared-icons'
SuggestionComponent,
HighlightPipe,
- AccountSetupModalComponent,
+ AccountSetupWarningModalComponent,
CustomModalComponent,
- WelcomeModalComponent,
+ AdminWelcomeModalComponent,
InstanceConfigWarningModalComponent,
ConfirmComponent
],
noInstanceConfigWarningModal: boolean
noWelcomeModal: boolean
+ noAccountSetupWarningModal: boolean
pluginAuth: string | null
this.noInstanceConfigWarningModal = hash.noInstanceConfigWarningModal
this.noWelcomeModal = hash.noWelcomeModal
+ this.noAccountSetupWarningModal = hash.noAccountSetupWarningModal
this.notificationSettings = hash.notificationSettings
+++ /dev/null
-import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
-import { AuthService, ServerService, User, UserService } from '@app/core'
-import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
-import { HTMLServerConfig } from '@shared/models'
-
-@Component({
- selector: 'my-account-setup-modal',
- templateUrl: './account-setup-modal.component.html',
- styleUrls: [ './account-setup-modal.component.scss' ]
-})
-export class AccountSetupModalComponent implements OnInit {
- @ViewChild('modal', { static: true }) modal: ElementRef
-
- user: User = null
- ref: NgbModalRef = null
-
- private serverConfig: HTMLServerConfig
-
- constructor (
- private userService: UserService,
- private authService: AuthService,
- private modalService: NgbModal,
- private serverService: ServerService
- ) { }
-
- get userInformationLoaded () {
- return this.authService.userInformationLoaded
- }
-
- get instanceName () {
- return this.serverConfig.instance.name
- }
-
- get isUserRoot () {
- return this.user.username === 'root'
- }
-
- get hasAccountAvatar () {
- return !!this.user.account.avatar
- }
-
- get hasAccountDescription () {
- return !!this.user.account.description
- }
-
- ngOnInit () {
- this.serverConfig = this.serverService.getHTMLConfig()
-
- this.authService.userInformationLoaded
- .subscribe(
- () => {
- this.user = this.authService.getUser()
-
- if (this.isUserRoot) return
- if (this.hasAccountAvatar && this.hasAccountDescription) return
- if (this.userService.hasSignupInThisSession()) return
-
- this.show()
- }
- )
- }
-
- show () {
- if (this.ref) return
-
- this.ref = this.modalService.open(this.modal, {
- centered: true,
- backdrop: 'static',
- keyboard: false,
- size: 'md'
- })
- }
-}
<p i18n>Help moderators and other users to know <strong>who you are</strong> by:</p>
<ul>
- <li *ngIf="!hasAccountAvatar" i18n>Uploading an <strong>avatar</strong></li>
- <li *ngIf="!hasAccountDescription" i18n>Writing a <strong>description</strong></li>
+ <li *ngIf="!hasAccountAvatar(user)" i18n>Uploading an <strong>avatar</strong></li>
+ <li *ngIf="!hasAccountDescription(user)" i18n>Writing a <strong>description</strong></li>
</ul>
</div>
<div class="modal-footer inputs">
+ <my-peertube-checkbox
+ inputName="stopDisplayModal" [(ngModel)]="stopDisplayModal"
+ i18n-labelText labelText="Don't show me this anymore"
+ >
+ </my-peertube-checkbox>
+
<input
type="button" role="button" i18n-value value="Remind me later" class="peertube-button grey-button"
(click)="hide()" (key.enter)="hide()"
--- /dev/null
+import { Component, ElementRef, ViewChild } from '@angular/core'
+import { Notifier, ServerService, User, UserService } from '@app/core'
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
+
+@Component({
+ selector: 'my-account-setup-warning-modal',
+ templateUrl: './account-setup-warning-modal.component.html',
+ styleUrls: [ './account-setup-warning-modal.component.scss' ]
+})
+export class AccountSetupWarningModalComponent {
+ @ViewChild('modal', { static: true }) modal: ElementRef
+
+ stopDisplayModal = false
+ ref: NgbModalRef
+
+ user: User
+
+ private LOCAL_STORAGE_KEYS = {
+ NO_ACCOUNT_SETUP_WARNING_MODAL: 'no_account_setup_warning_modal'
+ }
+
+ constructor (
+ private userService: UserService,
+ private modalService: NgbModal,
+ private notifier: Notifier,
+ private serverService: ServerService
+ ) { }
+
+ get instanceName () {
+ return this.serverService.getHTMLConfig().instance.name
+ }
+
+ hasAccountAvatar (user: User) {
+ return !!user.account.avatar
+ }
+
+ hasAccountDescription (user: User) {
+ return !!user.account.description
+ }
+
+ shouldOpen (user: User) {
+ if (user.noAccountSetupWarningModal === true) return false
+ if (peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_ACCOUNT_SETUP_WARNING_MODAL) === 'true') return false
+
+ if (this.hasAccountAvatar(user) && this.hasAccountDescription(user)) return false
+ if (this.userService.hasSignupInThisSession()) return false
+
+ return true
+ }
+
+ show (user: User) {
+ this.user = user
+
+ if (this.ref) return
+
+ this.ref = this.modalService.open(this.modal, {
+ centered: true,
+ backdrop: 'static',
+ keyboard: false,
+ size: 'md'
+ })
+
+ this.ref.result.finally(() => {
+ if (this.stopDisplayModal === true) this.doNotOpenAgain()
+ })
+ }
+
+ private doNotOpenAgain () {
+ peertubeLocalStorage.setItem(this.LOCAL_STORAGE_KEYS.NO_ACCOUNT_SETUP_WARNING_MODAL, 'true')
+
+ this.userService.updateMyProfile({ noAccountSetupWarningModal: true })
+ .subscribe({
+ next: () => console.log('We will not open the account setup modal again.'),
+
+ error: err => this.notifier.error(err.message)
+ })
+ }
+}
import { Component, ElementRef, ViewChild } from '@angular/core'
-import { Notifier, UserService } from '@app/core'
+import { Notifier, User, UserService } from '@app/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
@Component({
- selector: 'my-welcome-modal',
- templateUrl: './welcome-modal.component.html',
- styleUrls: [ './welcome-modal.component.scss' ]
+ selector: 'my-admin-welcome-modal',
+ templateUrl: './admin-welcome-modal.component.html',
+ styleUrls: [ './admin-welcome-modal.component.scss' ]
})
-export class WelcomeModalComponent {
+export class AdminWelcomeModalComponent {
@ViewChild('modal', { static: true }) modal: ElementRef
private LOCAL_STORAGE_KEYS = {
private notifier: Notifier
) { }
- show () {
- const result = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_WELCOME_MODAL)
- if (result === 'true') return
+ shouldOpen (user: User) {
+ if (user.noWelcomeModal === true) return false
+ if (peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_WELCOME_MODAL) === 'true') return false
+
+ return true
+ }
+ show () {
this.modalService.open(this.modal, {
centered: true,
backdrop: 'static',
import { Location } from '@angular/common'
import { Component, ElementRef, ViewChild } from '@angular/core'
-import { Notifier, UserService } from '@app/core'
+import { Notifier, User, UserService } from '@app/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
-import { About } from '@shared/models/server'
+import { About, ServerConfig } from '@shared/models/server'
@Component({
selector: 'my-instance-config-warning-modal',
private notifier: Notifier
) { }
- show (about: About) {
- const result = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_INSTANCE_CONFIG_WARNING_MODAL)
- if (result === 'true') return
+ shouldOpenByUser (user: User) {
+ if (user.noInstanceConfigWarningModal === true) return false
+ if (peertubeLocalStorage.getItem(this.LOCAL_STORAGE_KEYS.NO_INSTANCE_CONFIG_WARNING_MODAL) === 'true') return false
+
+ return true
+ }
+
+ shouldOpen (serverConfig: ServerConfig, about: About) {
+ if (!serverConfig.signup.allowed) return false
+ return serverConfig.instance.name.toLowerCase() === 'peertube' ||
+ !about.instance.terms ||
+ !about.instance.administrator ||
+ !about.instance.maintenanceLifetime
+ }
+
+ show (about: About) {
if (this.location.path().startsWith('/admin/config/edit-custom')) return
this.about = about
'videoLanguages',
'theme',
'noInstanceConfigWarningModal',
+ 'noAccountSetupWarningModal',
'noWelcomeModal'
]
return isBooleanValid(value)
}
-function isNoInstanceConfigWarningModal (value: any) {
- return isBooleanValid(value)
-}
-
-function isNoWelcomeModal (value: any) {
+function isUserNoModal (value: any) {
return isBooleanValid(value)
}
isUserAutoPlayNextVideoPlaylistValid,
isUserDisplayNameValid,
isUserDescriptionValid,
- isNoInstanceConfigWarningModal,
- isNoWelcomeModal
+ isUserNoModal
}
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 660
+const LAST_MIGRATION_VERSION = 665
// ---------------------------------------------------------------------------
--- /dev/null
+import * as Sequelize from 'sequelize'
+
+async function up (utils: {
+ transaction: Sequelize.Transaction
+ queryInterface: Sequelize.QueryInterface
+ sequelize: Sequelize.Sequelize
+ db: any
+}): Promise<void> {
+ {
+ const data = {
+ type: Sequelize.BOOLEAN,
+ allowNull: false,
+ defaultValue: false
+ }
+
+ await utils.queryInterface.addColumn('user', 'noAccountSetupWarningModal', data)
+ }
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
import { toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
import {
- isNoInstanceConfigWarningModal,
- isNoWelcomeModal,
isUserAdminFlagsValid,
isUserAutoPlayNextVideoValid,
isUserAutoPlayVideoValid,
isUserBlockedReasonValid,
isUserDescriptionValid,
isUserDisplayNameValid,
+ isUserNoModal,
isUserNSFWPolicyValid,
isUserPasswordValid,
isUserPasswordValidOrEmpty,
body('theme')
.optional()
.custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'),
+
body('noInstanceConfigWarningModal')
.optional()
- .custom(v => isNoInstanceConfigWarningModal(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'),
+ .custom(v => isUserNoModal(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'),
body('noWelcomeModal')
.optional()
- .custom(v => isNoWelcomeModal(v)).withMessage('Should have a valid noWelcomeModal boolean'),
+ .custom(v => isUserNoModal(v)).withMessage('Should have a valid noWelcomeModal boolean'),
+ body('noAccountSetupWarningModal')
+ .optional()
+ .custom(v => isUserNoModal(v)).withMessage('Should have a valid noAccountSetupWarningModal boolean'),
+
body('autoPlayNextVideo')
.optional()
.custom(v => isUserAutoPlayNextVideoValid(v)).withMessage('Should have a valid autoPlayNextVideo boolean'),
import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type'
import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
import {
- isNoInstanceConfigWarningModal,
- isNoWelcomeModal,
isUserAdminFlagsValid,
isUserAutoPlayNextVideoPlaylistValid,
isUserAutoPlayNextVideoValid,
isUserBlockedReasonValid,
isUserBlockedValid,
isUserEmailVerifiedValid,
+ isUserNoModal,
isUserNSFWPolicyValid,
isUserPasswordValid,
isUserRoleValid,
@Default(false)
@Is(
'UserNoInstanceConfigWarningModal',
- value => throwIfNotValid(value, isNoInstanceConfigWarningModal, 'no instance config warning modal')
+ value => throwIfNotValid(value, isUserNoModal, 'no instance config warning modal')
)
@Column
noInstanceConfigWarningModal: boolean
@AllowNull(false)
@Default(false)
@Is(
- 'UserNoInstanceConfigWarningModal',
- value => throwIfNotValid(value, isNoWelcomeModal, 'no welcome modal')
+ 'UserNoWelcomeModal',
+ value => throwIfNotValid(value, isUserNoModal, 'no welcome modal')
)
@Column
noWelcomeModal: boolean
+ @AllowNull(false)
+ @Default(false)
+ @Is(
+ 'UserNoAccountSetupWarningModal',
+ value => throwIfNotValid(value, isUserNoModal, 'no account setup warning modal')
+ )
+ @Column
+ noAccountSetupWarningModal: boolean
+
@AllowNull(true)
@Default(null)
@Column
noInstanceConfigWarningModal: this.noInstanceConfigWarningModal,
noWelcomeModal: this.noWelcomeModal,
+ noAccountSetupWarningModal: this.noAccountSetupWarningModal,
blocked: this.blocked,
blockedReason: this.blockedReason,
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
})
- it('Should fail with an invalid noInstanceConfigWarningModal attribute', async function () {
- const fields = {
- noInstanceConfigWarningModal: -1
- }
-
- await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
- })
+ it('Should fail with invalid no modal attributes', async function () {
+ const keys = [
+ 'noInstanceConfigWarningModal',
+ 'noAccountSetupWarningModal',
+ 'noWelcomeModal'
+ ]
+
+ for (const key of keys) {
+ const fields = {
+ [key]: -1
+ }
- it('Should fail with an invalid noWelcomeModal attribute', async function () {
- const fields = {
- noWelcomeModal: -1
+ await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
}
-
- await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
})
it('Should succeed to change password with the correct params', async function () {
email: 'super_email@example.com',
theme: 'default',
noInstanceConfigWarningModal: true,
- noWelcomeModal: true
+ noWelcomeModal: true,
+ noAccountSetupWarningModal: true
}
await makePutBodyRequest({
expect(user.account.description).to.equal('my super description updated')
expect(user.noWelcomeModal).to.be.false
expect(user.noInstanceConfigWarningModal).to.be.false
+ expect(user.noAccountSetupWarningModal).to.be.false
})
it('Should be able to update my theme', async function () {
await server.users.updateMe({
token: userToken,
noInstanceConfigWarningModal: true,
- noWelcomeModal: true
+ noWelcomeModal: true,
+ noAccountSetupWarningModal: true
})
const user = await server.users.getMyInfo({ token: userToken })
expect(user.noWelcomeModal).to.be.true
expect(user.noInstanceConfigWarningModal).to.be.true
+ expect(user.noAccountSetupWarningModal).to.be.true
})
})
noInstanceConfigWarningModal?: boolean
noWelcomeModal?: boolean
+ noAccountSetupWarningModal?: boolean
}
noInstanceConfigWarningModal: boolean
noWelcomeModal: boolean
+ noAccountSetupWarningModal: boolean
createdAt: Date
format: date-time
noInstanceConfigWarningModal:
type: boolean
+ noAccountSetupWarningModal:
+ type: boolean
noWelcomeModal:
type: boolean
nsfwPolicy:
type: string
noInstanceConfigWarningModal:
type: boolean
+ noAccountSetupWarningModal:
+ type: boolean
noWelcomeModal:
type: boolean
GetMeVideoRating: