From dfe3f7b72ef46401206f6f461077a7984a0c72f0 Mon Sep 17 00:00:00 2001 From: Kim <1877318+kimsible@users.noreply.github.com> Date: Tue, 28 Jul 2020 15:18:38 +0200 Subject: [PATCH] Add alert and hide upload view when no upload is possible (#2966) * Add alert and hide upload view when no upload is possible * Add about instance link to alert * Hide videos and imports links when no upload is possible * Correct curly spacing lint * Put logic canUpload to User model + add isHidden param to to-menu-dropdown * Use canSeeVideoLinks from user model * Rename and change logic canUpload to isUploadDisabled * Use isDisplayed() method intead of isHidden value * Refactor client and check videos count using quota Co-authored-by: kimsible Co-authored-by: Chocobozzz --- client/src/app/+admin/admin.component.ts | 20 ++++-- .../app/+my-account/my-account.component.ts | 33 ++++++--- .../+video-edit/video-add.component.html | 12 +++- .../+video-edit/video-add.component.scss | 28 ++++++++ .../+video-edit/video-add.component.ts | 8 ++- client/src/app/core/auth/auth-user.model.ts | 29 +++++++- client/src/app/core/users/user.model.ts | 4 ++ client/src/app/menu/menu.component.html | 2 +- client/src/app/menu/menu.component.ts | 67 +++++++++++++------ .../misc/top-menu-dropdown.component.html | 30 +++++---- .../misc/top-menu-dropdown.component.ts | 10 ++- 11 files changed, 186 insertions(+), 57 deletions(-) diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts index 319d50cda..7fc83351b 100644 --- a/client/src/app/+admin/admin.component.ts +++ b/client/src/app/+admin/admin.component.ts @@ -74,12 +74,24 @@ export class AdminComponent implements OnInit { }) } - if (this.hasUsersRight()) this.menuEntries.push({ label: this.i18n('Users'), routerLink: '/admin/users' }) + if (this.hasUsersRight()) { + this.menuEntries.push({ label: this.i18n('Users'), routerLink: '/admin/users' }) + } + if (this.hasServerFollowRight()) this.menuEntries.push(federationItems) if (this.hasAbusesRight() || this.hasVideoBlocklistRight()) this.menuEntries.push(moderationItems) - if (this.hasConfigRight()) this.menuEntries.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' }) - if (this.hasPluginsRight()) this.menuEntries.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' }) - if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) this.menuEntries.push({ label: this.i18n('System'), routerLink: '/admin/system' }) + + if (this.hasConfigRight()) { + this.menuEntries.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' }) + } + + if (this.hasPluginsRight()) { + this.menuEntries.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' }) + } + + if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) { + this.menuEntries.push({ label: this.i18n('System'), routerLink: '/admin/system' }) + } } hasUsersRight () { diff --git a/client/src/app/+my-account/my-account.component.ts b/client/src/app/+my-account/my-account.component.ts index 07cab37fc..5b2238f5a 100644 --- a/client/src/app/+my-account/my-account.component.ts +++ b/client/src/app/+my-account/my-account.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core' -import { ServerService } from '@app/core' +import { AuthService, ServerService, AuthUser } from '@app/core' import { I18n } from '@ngx-translate/i18n-polyfill' import { ServerConfig } from '@shared/models' import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdown.component' @@ -11,11 +11,13 @@ import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdo }) export class MyAccountComponent implements OnInit { menuEntries: TopMenuDropdownParam[] = [] + user: AuthUser private serverConfig: ServerConfig constructor ( private serverService: ServerService, + private authService: AuthService, private i18n: I18n ) { } @@ -24,6 +26,20 @@ export class MyAccountComponent implements OnInit { this.serverService.getConfig() .subscribe(config => this.serverConfig = config) + this.user = this.authService.getUser() + + this.authService.userInformationLoaded.subscribe( + () => this.buildMenu() + ) + } + + isVideoImportEnabled () { + const importConfig = this.serverConfig.import.videos + + return importConfig.http.enabled || importConfig.torrent.enabled + } + + private buildMenu () { const libraryEntries: TopMenuDropdownParam = { label: this.i18n('My library'), children: [ @@ -35,7 +51,8 @@ export class MyAccountComponent implements OnInit { { label: this.i18n('My videos'), routerLink: '/my-account/videos', - iconName: 'videos' + iconName: 'videos', + isDisplayed: () => this.user.canSeeVideosLink }, { label: this.i18n('My playlists'), @@ -45,7 +62,7 @@ export class MyAccountComponent implements OnInit { { label: this.i18n('My subscriptions'), routerLink: '/my-account/subscriptions', - iconName: 'subscriptions' + iconName: 'inbox-full' }, { label: this.i18n('My history'), @@ -59,7 +76,8 @@ export class MyAccountComponent implements OnInit { libraryEntries.children.push({ label: 'My imports', routerLink: '/my-account/video-imports', - iconName: 'cloud-download' + iconName: 'cloud-download', + isDisplayed: () => this.user.canSeeVideosLink }) } @@ -97,11 +115,4 @@ export class MyAccountComponent implements OnInit { miscEntries ] } - - isVideoImportEnabled () { - const importConfig = this.serverConfig.import.videos - - return importConfig.http.enabled || importConfig.torrent.enabled - } - } diff --git a/client/src/app/+videos/+video-edit/video-add.component.html b/client/src/app/+videos/+video-edit/video-add.component.html index 5690ac37f..14d41f95b 100644 --- a/client/src/app/+videos/+video-edit/video-add.component.html +++ b/client/src/app/+videos/+video-edit/video-add.component.html @@ -1,4 +1,12 @@ -
+
+
+
Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.
+ Read instance rules for help +
+ defeated mascot +
+ +
We recommend you to not use the root user to publish your videos, since it's the super-admin account of your instance.
@@ -45,4 +53,4 @@
-
+
\ No newline at end of file diff --git a/client/src/app/+videos/+video-edit/video-add.component.scss b/client/src/app/+videos/+video-edit/video-add.component.scss index f9977bda0..5db9e823d 100644 --- a/client/src/app/+videos/+video-edit/video-add.component.scss +++ b/client/src/app/+videos/+video-edit/video-add.component.scss @@ -6,6 +6,34 @@ $border-type: solid; $border-color: #EAEAEA; $nav-link-height: 40px; +.no-upload { + height: 100%; + width: 100%; + text-align: center; + + .about-link { + @include peertube-button-link; + @include orange-button; + + height: fit-content; + margin-top: 10px; + } + + img { + margin-top: 10px; + margin-bottom: 75px; + width: 220px; + height: auto; + } + + @media screen and (max-height: 600px) { + img { + margin-top: 5px; + width: 160px; + } + } +} + .margin-content { padding-top: 20px; } diff --git a/client/src/app/+videos/+video-edit/video-add.component.ts b/client/src/app/+videos/+video-edit/video-add.component.ts index 016791d59..94e85efc1 100644 --- a/client/src/app/+videos/+video-edit/video-add.component.ts +++ b/client/src/app/+videos/+video-edit/video-add.component.ts @@ -1,5 +1,5 @@ import { Component, HostListener, OnInit, ViewChild } from '@angular/core' -import { AuthService, CanComponentDeactivate, ServerService, User } from '@app/core' +import { AuthService, AuthUser, CanComponentDeactivate, ServerService } from '@app/core' import { ServerConfig } from '@shared/models' import { VideoImportTorrentComponent } from './video-add-components/video-import-torrent.component' import { VideoImportUrlComponent } from './video-add-components/video-import-url.component' @@ -15,7 +15,7 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate { @ViewChild('videoImportUrl') videoImportUrl: VideoImportUrlComponent @ViewChild('videoImportTorrent') videoImportTorrent: VideoImportTorrentComponent - user: User = null + user: AuthUser = null secondStepType: 'upload' | 'import-url' | 'import-torrent' videoName: string @@ -37,6 +37,8 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate { this.serverService.getConfig() .subscribe(config => this.serverConfig = config) + + this.user = this.auth.getUser() } onFirstStepDone (type: 'upload' | 'import-url' | 'import-torrent', videoName: string) { @@ -80,6 +82,6 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate { } isRootUser () { - return this.auth.getUser().username === 'root' + return this.user.username === 'root' } } diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts index 4e7801550..88b730938 100644 --- a/client/src/app/core/auth/auth-user.model.ts +++ b/client/src/app/core/auth/auth-user.model.ts @@ -1,3 +1,5 @@ +import { Observable, of } from 'rxjs' +import { map } from 'rxjs/operators' import { User } from '@app/core/users/user.model' import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' import { @@ -7,7 +9,8 @@ import { NSFWPolicyType, User as ServerUserModel, UserRight, - UserRole + UserRole, + UserVideoQuota } from '@shared/models' export type TokenOptions = { @@ -74,6 +77,8 @@ export class AuthUser extends User implements ServerMyUserModel { tokens: Tokens specialPlaylists: MyUserSpecialPlaylist[] + canSeeVideosLink = true + static load () { const usernameLocalStorage = peertubeLocalStorage.getItem(this.KEYS.USERNAME) if (usernameLocalStorage) { @@ -150,4 +155,26 @@ export class AuthUser extends User implements ServerMyUserModel { peertubeLocalStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo)) this.tokens.save() } + + computeCanSeeVideosLink (quotaObservable: Observable): Observable { + if (!this.isUploadDisabled()) { + this.canSeeVideosLink = true + return of(this.canSeeVideosLink) + } + + // Check if the user has videos + return quotaObservable.pipe( + map(({ videoQuotaUsed }) => { + if (videoQuotaUsed !== 0) { + // User already uploaded videos, so it can see the link + this.canSeeVideosLink = true + } else { + // No videos, no upload so the user don't need to see the videos link + this.canSeeVideosLink = false + } + + return this.canSeeVideosLink + }) + ) + } } diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts index 31b9c2152..6a56786d9 100644 --- a/client/src/app/core/users/user.model.ts +++ b/client/src/app/core/users/user.model.ts @@ -149,4 +149,8 @@ export class User implements UserServerModel { updateAccountAvatar (newAccountAvatar: Avatar) { this.account.updateAvatar(newAccountAvatar) } + + isUploadDisabled () { + return this.videoQuota === 0 || this.videoQuotaDaily === 0 + } } diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html index 8faa37ed6..71fb2c154 100644 --- a/client/src/app/menu/menu.component.html +++ b/client/src/app/menu/menu.component.html @@ -81,7 +81,7 @@
MY LIBRARY
- + Videos diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts index 0ea251f1c..2c55b9a84 100644 --- a/client/src/app/menu/menu.component.ts +++ b/client/src/app/menu/menu.component.ts @@ -1,21 +1,25 @@ import { HotkeysService } from 'angular2-hotkeys' +import * as debug from 'debug' +import { switchMap } from 'rxjs/operators' import { Component, OnInit, ViewChild } from '@angular/core' -import { AuthService, AuthStatus, RedirectService, ScreenService, ServerService, User, UserService } from '@app/core' +import { AuthService, AuthStatus, AuthUser, RedirectService, ScreenService, ServerService, UserService } from '@app/core' import { LanguageChooserComponent } from '@app/menu/language-chooser.component' import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component' import { I18n } from '@ngx-translate/i18n-polyfill' import { ServerConfig, UserRight, VideoConstant } from '@shared/models' +const logger = debug('peertube:menu:MenuComponent') + @Component({ selector: 'my-menu', templateUrl: './menu.component.html', - styleUrls: [ './menu.component.scss' ] + styleUrls: ['./menu.component.scss'] }) export class MenuComponent implements OnInit { @ViewChild('languageChooserModal', { static: true }) languageChooserModal: LanguageChooserComponent @ViewChild('quickSettingsModal', { static: true }) quickSettingsModal: QuickSettingsModalComponent - user: User + user: AuthUser isLoggedIn: boolean userHasAdminAccess = false @@ -25,7 +29,7 @@ export class MenuComponent implements OnInit { private languages: VideoConstant[] = [] private serverConfig: ServerConfig - private routesPerRight: { [ role in UserRight ]?: string } = { + private routesPerRight: { [role in UserRight]?: string } = { [UserRight.MANAGE_USERS]: '/admin/users', [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends', [UserRight.MANAGE_ABUSES]: '/admin/moderation/abuses', @@ -62,21 +66,30 @@ export class MenuComponent implements OnInit { .subscribe(config => this.serverConfig = config) this.isLoggedIn = this.authService.isLoggedIn() - if (this.isLoggedIn === true) this.user = this.authService.getUser() - this.computeIsUserHasAdminAccess() + if (this.isLoggedIn === true) { + this.user = this.authService.getUser() + this.computeVideosLink() + } + + this.computeAdminAccess() this.authService.loginChangedSource.subscribe( status => { if (status === AuthStatus.LoggedIn) { this.isLoggedIn = true this.user = this.authService.getUser() - this.computeIsUserHasAdminAccess() - console.log('Logged in.') + + this.computeAdminAccess() + this.computeVideosLink() + + logger('Logged in.') } else if (status === AuthStatus.LoggedOut) { this.isLoggedIn = false this.user = undefined - this.computeIsUserHasAdminAccess() - console.log('Logged out.') + + this.computeAdminAccess() + + logger('Logged out.') } else { console.error('Unknown auth status: ' + status) } @@ -84,15 +97,15 @@ export class MenuComponent implements OnInit { ) this.hotkeysService.cheatSheetToggle - .subscribe(isOpen => this.helpVisible = isOpen) + .subscribe(isOpen => this.helpVisible = isOpen) this.serverService.getVideoLanguages() - .subscribe(languages => { - this.languages = languages + .subscribe(languages => { + this.languages = languages - this.authService.userInformationLoaded - .subscribe(() => this.buildUserLanguages()) - }) + this.authService.userInformationLoaded + .subscribe(() => this.buildUserLanguages()) + }) } get language () { @@ -116,7 +129,7 @@ export class MenuComponent implements OnInit { isRegistrationAllowed () { return this.serverConfig.signup.allowed && - this.serverConfig.signup.allowedForCurrentIP + this.serverConfig.signup.allowedForCurrentIP } getFirstAdminRightAvailable () { @@ -172,7 +185,7 @@ export class MenuComponent implements OnInit { this.user.webTorrentEnabled = !this.user.webTorrentEnabled this.userService.updateMyProfile({ webTorrentEnabled: this.user.webTorrentEnabled }) - .subscribe(() => this.authService.refreshUserInformation()) + .subscribe(() => this.authService.refreshUserInformation()) } langForLocale (localeId: string) { @@ -188,18 +201,28 @@ export class MenuComponent implements OnInit { } if (!this.user.videoLanguages) { - this.videoLanguages = [ this.i18n('any language') ] + this.videoLanguages = [this.i18n('any language')] return } this.videoLanguages = this.user.videoLanguages - .map(locale => this.langForLocale(locale)) - .map(value => value === undefined ? '?' : value) + .map(locale => this.langForLocale(locale)) + .map(value => value === undefined ? '?' : value) } - private computeIsUserHasAdminAccess () { + private computeAdminAccess () { const right = this.getFirstAdminRightAvailable() this.userHasAdminAccess = right !== undefined } + + private computeVideosLink () { + this.authService.userInformationLoaded + .pipe( + switchMap(() => this.user.computeCanSeeVideosLink(this.userService.getMyVideoQuotaUsed())) + ).subscribe(res => { + if (res === true) logger('User can see videos link.') + else logger('User cannot see videos link.') + }) + } } diff --git a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html index c737b40c7..530b9e376 100644 --- a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html +++ b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html @@ -1,9 +1,9 @@