Add welcome modal

This commit is contained in:
Chocobozzz
2019-08-28 14:40:06 +02:00
committed by Chocobozzz
parent bc20aaed46
commit 43d0ea7f4b
20 changed files with 428 additions and 67 deletions

View File

@@ -54,3 +54,8 @@
</div>
</ng-template>
</p-toast>
<ng-template [ngIf]="isUserLoggedIn()">
<my-welcome-modal #welcomeModal></my-welcome-modal>
<my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal>
</ng-template>

View File

@@ -1,10 +1,10 @@
import { Component, OnInit } from '@angular/core'
import { Component, OnInit, ViewChild } from '@angular/core'
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
import { Event, GuardsCheckStart, NavigationEnd, Router, Scroll } from '@angular/router'
import { AuthService, RedirectService, ServerService, ThemeService } from '@app/core'
import { is18nPath } from '../../../shared/models/i18n'
import { ScreenService } from '@app/shared/misc/screen.service'
import { debounceTime, filter, map, pairwise, skip } from 'rxjs/operators'
import { debounceTime, filter, map, pairwise, skip, switchMap } from 'rxjs/operators'
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { fromEvent } from 'rxjs'
@@ -13,6 +13,11 @@ import { PluginService } from '@app/core/plugins/plugin.service'
import { HooksService } from '@app/core/plugins/hooks.service'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { POP_STATE_MODAL_DISMISS } from '@app/shared/misc/constants'
import { WelcomeModalComponent } from '@app/modal/welcome-modal.component'
import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component'
import { UserRole } from '@shared/models'
import { User } from '@app/shared'
import { InstanceService } from '@app/shared/instance/instance.service'
@Component({
selector: 'my-app',
@@ -20,6 +25,9 @@ import { POP_STATE_MODAL_DISMISS } from '@app/shared/misc/constants'
styleUrls: [ './app.component.scss' ]
})
export class AppComponent implements OnInit {
@ViewChild('welcomeModal', { static: false }) welcomeModal: WelcomeModalComponent
@ViewChild('instanceConfigWarningModal', { static: false }) instanceConfigWarningModal: InstanceConfigWarningModalComponent
isMenuDisplayed = true
isMenuChangedByUser = false
@@ -32,6 +40,7 @@ export class AppComponent implements OnInit {
private authService: AuthService,
private serverService: ServerService,
private pluginService: PluginService,
private instanceService: InstanceService,
private domSanitizer: DomSanitizer,
private redirectService: RedirectService,
private screenService: ScreenService,
@@ -96,6 +105,8 @@ export class AppComponent implements OnInit {
.subscribe(() => this.onResize())
this.location.onPopState(() => this.modalService.dismissAll(POP_STATE_MODAL_DISMISS))
this.openModalsIfNeeded()
}
isUserLoggedIn () {
@@ -220,32 +231,62 @@ export class AppComponent implements OnInit {
this.hooks.runAction('action:application.init', 'common')
}
private async openModalsIfNeeded () {
this.serverService.configLoaded
.pipe(
switchMap(() => this.authService.userInformationLoaded),
map(() => this.authService.getUser()),
filter(user => user.role === UserRole.ADMINISTRATOR)
).subscribe(user => setTimeout(() => this.openAdminModals(user))) // setTimeout because of ngIf in template
}
private async openAdminModals (user: User) {
if (user.noWelcomeModal !== true) return this.welcomeModal.show()
const config = this.serverService.getConfig()
if (user.noInstanceConfigWarningModal !== true && config.signup.allowed && config.instance.name.toLowerCase() === 'peertube') {
this.instanceService.getAbout()
.subscribe(about => {
if (!about.instance.terms) {
this.instanceConfigWarningModal.show()
}
})
}
}
private initHotkeys () {
this.hotkeysService.add([
new Hotkey(['/', 's'], (event: KeyboardEvent): boolean => {
document.getElementById('search-video').focus()
return false
}, undefined, this.i18n('Focus the search bar')),
new Hotkey('b', (event: KeyboardEvent): boolean => {
this.toggleMenu()
return false
}, undefined, this.i18n('Toggle the left menu')),
new Hotkey('g o', (event: KeyboardEvent): boolean => {
this.router.navigate([ '/videos/overview' ])
return false
}, undefined, this.i18n('Go to the discover videos page')),
new Hotkey('g t', (event: KeyboardEvent): boolean => {
this.router.navigate([ '/videos/trending' ])
return false
}, undefined, this.i18n('Go to the trending videos page')),
new Hotkey('g r', (event: KeyboardEvent): boolean => {
this.router.navigate([ '/videos/recently-added' ])
return false
}, undefined, this.i18n('Go to the recently added videos page')),
new Hotkey('g l', (event: KeyboardEvent): boolean => {
this.router.navigate([ '/videos/local' ])
return false
}, undefined, this.i18n('Go to the local videos page')),
new Hotkey('g u', (event: KeyboardEvent): boolean => {
this.router.navigate([ '/videos/upload' ])
return false

View File

@@ -18,6 +18,8 @@ import { VideosModule } from './videos'
import { buildFileLocale, getCompleteLocale, isDefaultLocale } from '../../../shared/models/i18n'
import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
import { SearchModule } from '@app/search'
import { WelcomeModalComponent } from '@app/modal/welcome-modal.component'
import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component'
export function metaFactory (serverService: ServerService): MetaLoader {
return new MetaStaticLoader({
@@ -39,7 +41,10 @@ export function metaFactory (serverService: ServerService): MetaLoader {
MenuComponent,
LanguageChooserComponent,
AvatarNotificationComponent,
HeaderComponent
HeaderComponent,
WelcomeModalComponent,
InstanceConfigWarningModalComponent
],
imports: [
BrowserModule,

View File

@@ -0,0 +1,15 @@
<ng-template #modal let-hide="close">
<div class="modal-header">
<h4 i18n class="modal-title">Warning!</h4>
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
</div>
<div class="modal-body">
</div>
<div class="modal-footer inputs">
<span i18n class="action-button action-button-cancel" (click)="hide()">Close</span>
</div>
</ng-template>

View File

@@ -0,0 +1,6 @@
@import '_mixins';
@import '_variables';
.action-button-cancel {
margin-right: 0 !important;
}

View File

@@ -0,0 +1,23 @@
import { Component, ElementRef, ViewChild } from '@angular/core'
import { Notifier } from '@app/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'my-instance-config-warning-modal',
templateUrl: './instance-config-warning-modal.component.html',
styleUrls: [ './instance-config-warning-modal.component.scss' ]
})
export class InstanceConfigWarningModalComponent {
@ViewChild('modal', { static: true }) modal: ElementRef
constructor (
private modalService: NgbModal,
private notifier: Notifier,
private i18n: I18n
) { }
show () {
this.modalService.open(this.modal)
}
}

View File

@@ -0,0 +1,66 @@
<ng-template #modal let-hide="close">
<div class="modal-header">
<h4 i18n class="modal-title">Welcome on PeerTube dear administrator!</h4>
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
</div>
<div class="modal-body">
<div class="block-links">
<div class="subtitle">Useful links</div>
<ul>
<li>
Official PeerTube website: <a href="https://joinpeertube.org" target="_blank" rel="noopener noreferrer">https://joinpeertube.org</a>
</li>
<li>
Discover CLI PeerTube tools (to upload or import videos, parse logs, prune storage directories, reset user password...):
<a href="https://docs.joinpeertube.org/#/maintain-tools" target="_blank" rel="noopener noreferrer">https://docs.joinpeertube.org/#/maintain-tools</a>
</li>
<li>
Understand how to administer your instance (managing users, following other instances, dealing with spammers...):
<a href="https://docs.joinpeertube.org/#/admin-following-instances" target="_blank" rel="noopener noreferrer">https://docs.joinpeertube.org/#/admin-following-instances</a>
</li>
<li>
Learn how to use PeerTube (setup your account, managing video playlists, discover third-party applications...):
<a href="https://docs.joinpeertube.org/#/use-setup-account" target="_blank" rel="noopener noreferrer">https://docs.joinpeertube.org/#/use-setup-account</a>
</li>
</ul>
</div>
<div class="block-configuration">
<div class="subtitle">Configure your instance</div>
<p>
Now it's time to configure your instance! Choosing your <strong>instance name</strong>, <strong>setting up a description</strong>,
specifying <strong>who you are</strong> and <strong>how long</strong> you plan to <strong>maintain your instance</strong>
is very important for visitors to understand on what type of instance they are.
</p>
<p>
If you want to open registrations, please decide what are <strong>your moderation rules</strong>, fill your <strong>instance terms</strong>
and specify the categories and languages you speak. This way, users that are looking for a PeerTube instance on which they can register
will be able to choose <strong>the right one</strong>.
</p>
<div class="configure-instance">
<a href="/admin/config/edit-custom" target="_blank" rel="noopener noreferrer">Configure your instance</a>
</div>
</div>
<div class="block-instance">
<div class="subtitle">Index your instance</div>
If you want, you can index your PeerTube instance on the public PeerTube instances list:
<a href="https://instances.joinpeertube.org/instances">https://instances.joinpeertube.org/instances</a>
</div>
</div>
<div class="modal-footer inputs">
<span i18n class="action-button action-button-submit" (click)="hide()">Understood!</span>
</div>
</ng-template>

View File

@@ -0,0 +1,31 @@
@import '_mixins';
@import '_variables';
.modal-body {
font-size: 15px;
}
.action-button-cancel {
margin-right: 0 !important;
}
.subtitle {
font-weight: $font-semibold;
margin-bottom: 10px;
font-size: 16px;
}
.block-configuration,
.block-instance {
margin-top: 30px;
}
li {
margin-bottom: 10px;
}
.configure-instance {
text-align: center;
font-weight: 600;
font-size: 18px;
}

View File

@@ -0,0 +1,40 @@
import { Component, ElementRef, ViewChild } from '@angular/core'
import { Notifier } from '@app/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { UserService } from '@app/shared'
@Component({
selector: 'my-welcome-modal',
templateUrl: './welcome-modal.component.html',
styleUrls: [ './welcome-modal.component.scss' ]
})
export class WelcomeModalComponent {
@ViewChild('modal', { static: true }) modal: ElementRef
constructor (
private userService: UserService,
private modalService: NgbModal,
private notifier: Notifier
) { }
show () {
const ref = this.modalService.open(this.modal,{
backdrop: 'static',
keyboard: false,
size: 'lg'
})
ref.result.finally(() => this.doNotOpenAgain())
}
private doNotOpenAgain () {
this.userService.updateMyProfile({ noWelcomeModal: true })
.subscribe(
() => console.log('We will not open the welcome modal again.'),
err => this.notifier.error(err.message)
)
return true
}
}

View File

@@ -9,31 +9,38 @@ export class User implements UserServerModel {
username: string
email: string
pendingEmail: string | null
emailVerified: boolean
nsfwPolicy: NSFWPolicyType
adminFlags?: UserAdminFlag
autoPlayVideo: boolean
webTorrentEnabled: boolean
videosHistoryEnabled: boolean
videoLanguages: string[]
role: UserRole
roleLabel: string
webTorrentEnabled: boolean
autoPlayVideo: boolean
videosHistoryEnabled: boolean
videoLanguages: string[]
videoQuota: number
videoQuotaDaily: number
account: Account
videoChannels: VideoChannel[]
createdAt: Date
videoQuotaUsed?: number
videoQuotaUsedDaily?: number
theme: string
adminFlags?: UserAdminFlag
account: Account
notificationSettings?: UserNotificationSetting
videoChannels?: VideoChannel[]
blocked: boolean
blockedReason?: string
notificationSettings?: UserNotificationSetting
noInstanceConfigWarningModal: boolean
noWelcomeModal: boolean
createdAt: Date
constructor (hash: Partial<UserServerModel>) {
this.id = hash.id
@@ -43,13 +50,16 @@ export class User implements UserServerModel {
this.role = hash.role
this.videoChannels = hash.videoChannels
this.videoQuota = hash.videoQuota
this.videoQuotaDaily = hash.videoQuotaDaily
this.videoQuotaUsed = hash.videoQuotaUsed
this.videoQuotaUsedDaily = hash.videoQuotaUsedDaily
this.nsfwPolicy = hash.nsfwPolicy
this.webTorrentEnabled = hash.webTorrentEnabled
this.videosHistoryEnabled = hash.videosHistoryEnabled
this.autoPlayVideo = hash.autoPlayVideo
this.createdAt = hash.createdAt
this.theme = hash.theme
@@ -58,8 +68,13 @@ export class User implements UserServerModel {
this.blocked = hash.blocked
this.blockedReason = hash.blockedReason
this.noInstanceConfigWarningModal = hash.noInstanceConfigWarningModal
this.noWelcomeModal = hash.noWelcomeModal
this.notificationSettings = hash.notificationSettings
this.createdAt = hash.createdAt
if (hash.account !== undefined) {
this.account = new Account(hash.account)
}