Inform user to fill account profile and channels (#4352)

* Add account-setup modal when login

* Add channels-setup alert into my-channels, my-playlists and upload page

Co-authored-by: Ms Kimsible <kimsible@users.noreply.github.com>
This commit is contained in:
Ms Kimsible 2021-08-26 08:22:33 +02:00 committed by GitHub
parent 8729a87024
commit 7dca45f99d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 232 additions and 4 deletions

View file

@ -526,7 +526,7 @@
# Icons
* [Feather Icons](https://feathericons.com) (MIT)
* `playlist add`, `history`, `subscriptions`, `miscellaneous-services.svg` by Material UI (Apache 2.0)
* `playlist add`, `history`, `subscriptions`, `miscellaneous-services.svg`, `tip` by Material UI (Apache 2.0)
* `support` by Chocobozzz (CC-BY)
* `language` by Aaron Jin (CC-BY)
* `video-language` by Rigel Kent (CC-BY)

View file

@ -4,6 +4,8 @@
<span class="badge badge-secondary">{{ totalItems }}</span>
</h1>
<my-channels-setup-message [hideLink]="true"></my-channels-setup-message>
<div class="video-channels-header d-flex justify-content-between">
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>

View file

@ -3,6 +3,8 @@
<ng-container i18n>My playlists</ng-container> <span class="badge badge-secondary">{{ pagination.totalItems }}</span>
</h1>
<my-channels-setup-message></my-channels-setup-message>
<div class="video-playlists-header d-flex justify-content-between">
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>

View file

@ -45,6 +45,8 @@
<ng-container *ngIf="secondStepType === 'upload'" i18n>Upload {{ videoName }}</ng-container>
</div>
<my-channels-setup-message></my-channels-setup-message>
<div ngbNav #nav="ngbNav" class="nav-tabs video-add-nav" [activeId]="activeNav" (activeIdChange)="onNavChange($event)" [ngClass]="{ 'hide-nav': !!secondStepType }">
<ng-container ngbNavItem="upload">
<a ngbNavLink>

View file

@ -60,9 +60,10 @@
</ng-template>
</p-toast>
<ng-template [ngIf]="isUserLoggedIn()">
<ng-container *ngIf="isUserLoggedIn()">
<my-account-setup-modal #accountSetupModal></my-account-setup-modal>
<my-welcome-modal #welcomeModal></my-welcome-modal>
<my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal>
</ng-template>
</ng-container>
<my-custom-modal #customModal></my-custom-modal>

View file

@ -20,6 +20,7 @@ import { PluginService } from '@app/core/plugins/plugin.service'
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 { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { LoadingBarService } from '@ngx-loading-bar/core'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
@ -37,6 +38,7 @@ 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('instanceConfigWarningModal') instanceConfigWarningModal: InstanceConfigWarningModalComponent
@ViewChild('customModal') customModal: CustomModalComponent

View file

@ -18,6 +18,7 @@ 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 { SharedActorImageModule } from './shared/shared-actor-image/shared-actor-image.module'
import { SharedFormModule } from './shared/shared-forms'
import { SharedGlobalIconModule } from './shared/shared-icons'
@ -53,6 +54,7 @@ export function loadConfigFactory (server: ServerService, pluginService: PluginS
SuggestionComponent,
HighlightPipe,
AccountSetupModalComponent,
CustomModalComponent,
WelcomeModalComponent,
InstanceConfigWarningModalComponent,

View file

@ -0,0 +1,33 @@
<ng-template #modal let-hide="close">
<div class="modal-header">
<h4 i18n class="modal-title">Welcome to {{ instanceName }}, dear user!</h4>
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
</div>
<div class="modal-body">
<img class="mascot" src="/client/assets/images/mascot/happy.svg" alt="mascot">
<div i18n class="subtitle">It's time to set up your account profile!</div>
<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>
</ul>
</div>
<div class="modal-footer inputs">
<input
type="button" role="button" i18n-value value="Remind me later" class="peertube-button grey-button"
(click)="hide()" (key.enter)="hide()"
>
<a i18n (click)="hide()" (key.enter)="hide()"
class="peertube-button-link orange-button" routerLink="/my-account"
rel="noopener noreferrer" ngbAutofocus>
Set up
</a>
</div>
</ng-template>

View file

@ -0,0 +1,31 @@
@use '_mixins' as *;
@use '_variables' as *;
.modal-body {
font-size: 15px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.mascot-fw {
width: 170px;
}
.mascot {
@include margin-right(2rem);
display: block;
min-width: 170px;
}
.subtitle {
font-weight: $font-semibold;
margin-bottom: 10px;
font-size: 16px;
}
li {
margin-bottom: 10px;
}

View file

@ -0,0 +1,70 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
import { AuthService, ServerService, User } 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 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.user = this.authService.getUser()
this.authService.userInformationLoaded
.subscribe(
() => {
if (this.isUserRoot) return false
if (this.hasAccountAvatar && this.hasAccountDescription) return false
this.show()
}
)
}
show () {
if (this.ref) return false
this.ref = this.modalService.open(this.modal, {
centered: true,
backdrop: 'static',
keyboard: false,
size: 'md'
})
}
}

View file

@ -16,6 +16,7 @@ const icons = {
'playlist-add': require('!!raw-loader?!../../../assets/images/misc/playlist-add.svg').default, // material ui
follower: require('!!raw-loader?!../../../assets/images/misc/account-arrow-left.svg').default, // material ui
following: require('!!raw-loader?!../../../assets/images/misc/account-arrow-right.svg').default, // material ui
tip: require('!!raw-loader?!../../../assets/images/misc/tip.svg').default, // material ui
flame: require('!!raw-loader?!../../../assets/images/misc/flame.svg').default,
local: require('!!raw-loader?!../../../assets/images/misc/local.svg').default,

View file

@ -0,0 +1,8 @@
<div *ngIf="hasChannelNotConfigured" class="channels-setup-message alert alert-info">
<my-global-icon iconName="tip"></my-global-icon>
<div>
<div i18n>Some of your channels are not fully set up. Make them welcoming and explicit about what you publish by adding a <strong>banner</strong>, an <strong>avatar</strong> and a <strong>description</strong>.</div>
<a *ngIf="!hideLink" class="channels-settings-link" routerLink="/my-library/video-channels" i18n>Set up my channels</a>
</div>
</div>

View file

@ -0,0 +1,32 @@
@use '_variables' as *;
@use '_mixins' as *;
.channels-setup-message {
display: flex;
align-items: center;
justify-content: center;
my-global-icon {
width: 32px;
align-self: flex-start;
::ng-deep {
svg {
fill: #0c5460;
}
}
+ div {
margin-left: 10px;
text-align: center;
a.channels-settings-link {
@include peertube-button-link;
@include grey-button;
height: fit-content;
margin-top: 10px;
}
}
}
}

View file

@ -0,0 +1,32 @@
import { Component, Input, OnInit } from '@angular/core'
import { AuthService, User } from '@app/core'
import { VideoChannel } from '@app/shared/shared-main'
@Component({
selector: 'my-channels-setup-message',
templateUrl: './channels-setup-message.component.html',
styleUrls: [ './channels-setup-message.component.scss' ]
})
export class ChannelsSetupMessageComponent implements OnInit {
@Input() hideLink = false
user: User = null
constructor (
private authService: AuthService
) {}
get userInformationLoaded () {
return this.authService.userInformationLoaded
}
get hasChannelNotConfigured () {
return this.user.videoChannels
.filter((channel: VideoChannel) => (!channel.avatar || !channel.description))
.length > 0
}
ngOnInit () {
this.user = this.authService.getUser()
}
}

View file

@ -1,3 +1,4 @@
export * from './channels-setup-message.component'
export * from './help.component'
export * from './list-overflow.component'
export * from './top-menu-dropdown.component'

View file

@ -34,7 +34,13 @@ import { CustomPageService } from './custom-page'
import { DateToggleComponent } from './date'
import { FeedComponent } from './feeds'
import { LoaderComponent, SmallLoaderComponent } from './loaders'
import { HelpComponent, ListOverflowComponent, SimpleSearchInputComponent, TopMenuDropdownComponent } from './misc'
import {
ChannelsSetupMessageComponent,
HelpComponent,
ListOverflowComponent,
SimpleSearchInputComponent,
TopMenuDropdownComponent
} from './misc'
import { PluginPlaceholderComponent } from './plugins'
import { ActorRedirectGuard } from './router'
import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users'
@ -91,6 +97,7 @@ import { VideoChannelService } from './video-channel'
LoaderComponent,
SmallLoaderComponent,
ChannelsSetupMessageComponent,
HelpComponent,
ListOverflowComponent,
TopMenuDropdownComponent,
@ -146,6 +153,7 @@ import { VideoChannelService } from './video-channel'
LoaderComponent,
SmallLoaderComponent,
ChannelsSetupMessageComponent,
HelpComponent,
ListOverflowComponent,
TopMenuDropdownComponent,

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><g><rect height="3" width="2" x="11" y="19"/><rect height="2" width="3" x="2" y="11"/><rect height="2" width="3" x="19" y="11"/><rect height="3" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -7.6665 17.8014)" width="1.99" x="16.66" y="16.66"/><rect height="1.99" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -10.9791 9.8041)" width="3" x="4.85" y="17.16"/><path d="M15,8.02V3H9v5.02C7.79,8.94,7,10.37,7,12c0,2.76,2.24,5,5,5s5-2.24,5-5C17,10.37,16.21,8.94,15,8.02z M11,5h2v2.1 C12.68,7.04,12.34,7,12,7s-0.68,0.04-1,0.1V5z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 726 B