1
0
Fork 0

Reorganize left menu and account menu

Add my-settings and my-library in left menu
Move administration below my-library
Split account menu: my-setting and my library
This commit is contained in:
Chocobozzz 2020-11-12 15:28:54 +01:00 committed by Chocobozzz
parent b4bc269e55
commit 17119e4a54
68 changed files with 854 additions and 608 deletions

View File

@ -3,15 +3,15 @@ import { by, element, browser } from 'protractor'
export class MyAccountPage {
navigateToMyVideos () {
return element(by.css('a[href="/my-account/videos"]')).click()
return element(by.css('a[href="/my-library/videos"]')).click()
}
navigateToMyPlaylists () {
return element(by.css('a[href="/my-account/video-playlists"]')).click()
return element(by.css('a[href="/my-library/video-playlists"]')).click()
}
navigateToMyHistory () {
return element(by.css('a[href="/my-account/history/videos"]')).click()
return element(by.css('a[href="/my-library/history/videos"]')).click()
}
// My account Videos

View File

@ -1,41 +0,0 @@
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
import { MyAccountVideoChannelUpdateComponent } from './my-account-video-channel-update.component'
import { MyAccountVideoChannelCreateComponent } from './my-account-video-channel-create.component'
import { MyAccountVideoChannelsComponent } from './my-account-video-channels.component'
const myAccountVideoChannelsRoutes: Routes = [
{
path: '',
component: MyAccountVideoChannelsComponent,
data: {
meta: {
title: $localize`Account video channels`
}
}
},
{
path: 'create',
component: MyAccountVideoChannelCreateComponent,
data: {
meta: {
title: $localize`Create new video channel`
}
}
},
{
path: 'update/:videoChannelId',
component: MyAccountVideoChannelUpdateComponent,
data: {
meta: {
title: $localize`Update video channel`
}
}
}
]
@NgModule({
imports: [ RouterModule.forChild(myAccountVideoChannelsRoutes) ],
exports: [ RouterModule ]
})
export class MyAccountVideoChannelsRoutingModule {}

View File

@ -1,31 +0,0 @@
import { ChartModule } from 'primeng/chart'
import { NgModule } from '@angular/core'
import { SharedFormModule } from '@app/shared/shared-forms'
import { SharedGlobalIconModule } from '@app/shared/shared-icons'
import { SharedMainModule } from '@app/shared/shared-main'
import { MyAccountVideoChannelCreateComponent } from './my-account-video-channel-create.component'
import { MyAccountVideoChannelUpdateComponent } from './my-account-video-channel-update.component'
import { MyAccountVideoChannelsRoutingModule } from './my-account-video-channels-routing.module'
import { MyAccountVideoChannelsComponent } from './my-account-video-channels.component'
@NgModule({
imports: [
MyAccountVideoChannelsRoutingModule,
ChartModule,
SharedMainModule,
SharedFormModule,
SharedGlobalIconModule
],
declarations: [
MyAccountVideoChannelsComponent,
MyAccountVideoChannelCreateComponent,
MyAccountVideoChannelUpdateComponent
],
exports: [],
providers: []
})
export class MyAccountVideoChannelsModule { }

View File

@ -2,21 +2,12 @@ import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
import { MetaGuard } from '@ngx-meta/core'
import { LoginGuard } from '../core'
import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component'
import { MyAccountBlocklistComponent } from './my-account-blocklist/my-account-blocklist.component'
import { MyAccountServerBlocklistComponent } from './my-account-blocklist/my-account-server-blocklist.component'
import { MyAccountHistoryComponent } from './my-account-history/my-account-history.component'
import { MyAccountNotificationsComponent } from './my-account-notifications/my-account-notifications.component'
import { MyAccountOwnershipComponent } from './my-account-ownership/my-account-ownership.component'
import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component'
import { MyAccountSubscriptionsComponent } from './my-account-subscriptions/my-account-subscriptions.component'
import { MyAccountVideoImportsComponent } from './my-account-video-imports/my-account-video-imports.component'
import { MyAccountVideoPlaylistCreateComponent } from './my-account-video-playlists/my-account-video-playlist-create.component'
import { MyAccountVideoPlaylistElementsComponent } from './my-account-video-playlists/my-account-video-playlist-elements.component'
import { MyAccountVideoPlaylistUpdateComponent } from './my-account-video-playlists/my-account-video-playlist-update.component'
import { MyAccountVideoPlaylistsComponent } from './my-account-video-playlists/my-account-video-playlists.component'
import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component'
import { MyAccountComponent } from './my-account.component'
import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component'
const myAccountRoutes: Routes = [
{
@ -41,88 +32,50 @@ const myAccountRoutes: Routes = [
{
path: 'video-channels',
loadChildren: () => {
return import('./+my-account-video-channels/my-account-video-channels.module')
.then(m => m.MyAccountVideoChannelsModule)
}
redirectTo: '/my-library/video-channels',
pathMatch: 'full'
},
{
path: 'video-playlists',
component: MyAccountVideoPlaylistsComponent,
data: {
meta: {
title: $localize`Account playlists`
}
}
redirectTo: '/my-library/video-playlists',
pathMatch: 'full'
},
{
path: 'video-playlists/create',
component: MyAccountVideoPlaylistCreateComponent,
data: {
meta: {
title: $localize`Create new playlist`
}
}
redirectTo: '/my-library/video-playlists/create',
pathMatch: 'full'
},
{
path: 'video-playlists/:videoPlaylistId',
component: MyAccountVideoPlaylistElementsComponent,
data: {
meta: {
title: $localize`Playlist elements`
}
}
redirectTo: '/my-library/video-playlists/:videoPlaylistId',
pathMatch: 'full'
},
{
path: 'video-playlists/update/:videoPlaylistId',
component: MyAccountVideoPlaylistUpdateComponent,
data: {
meta: {
title: $localize`Update playlist`
}
}
redirectTo: '/my-library/video-playlists/update/:videoPlaylistId',
pathMatch: 'full'
},
{
path: 'videos',
component: MyAccountVideosComponent,
data: {
meta: {
title: $localize`Account videos`
},
reuse: {
enabled: true,
key: 'my-account-videos-list'
}
}
redirectTo: '/my-library/videos',
pathMatch: 'full'
},
{
path: 'video-imports',
component: MyAccountVideoImportsComponent,
data: {
meta: {
title: $localize`Account video imports`
}
}
redirectTo: '/my-library/video-imports',
pathMatch: 'full'
},
{
path: 'subscriptions',
component: MyAccountSubscriptionsComponent,
data: {
meta: {
title: $localize`Account subscriptions`
}
}
redirectTo: '/my-library/subscriptions',
pathMatch: 'full'
},
{
path: 'ownership',
component: MyAccountOwnershipComponent,
data: {
meta: {
title: $localize`Ownership changes`
}
}
redirectTo: '/my-library/ownership',
pathMatch: 'full'
},
{
path: 'blocklist/accounts',
@ -144,16 +97,8 @@ const myAccountRoutes: Routes = [
},
{
path: 'history/videos',
component: MyAccountHistoryComponent,
data: {
meta: {
title: $localize`Videos history`
},
reuse: {
enabled: true,
key: 'my-videos-history-list'
}
}
redirectTo: '/my-library/history/videos',
pathMatch: 'full'
},
{
path: 'notifications',

View File

@ -1,6 +1,5 @@
import { Component, OnInit } from '@angular/core'
import { AuthService, AuthUser, ScreenService, ServerService } from '@app/core'
import { ServerConfig } from '@shared/models'
import { AuthUser, ScreenService } from '@app/core'
import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdown.component'
@Component({
@ -12,11 +11,7 @@ export class MyAccountComponent implements OnInit {
menuEntries: TopMenuDropdownParam[] = []
user: AuthUser
private serverConfig: ServerConfig
constructor (
private serverService: ServerService,
private authService: AuthService,
private screenService: ScreenService
) { }
@ -25,67 +20,12 @@ export class MyAccountComponent implements OnInit {
}
ngOnInit (): void {
this.serverConfig = this.serverService.getTmpConfig()
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
this.buildMenu()
}
private buildMenu () {
const libraryEntries: TopMenuDropdownParam = {
label: $localize`My library`,
children: [
{
label: $localize`My channels`,
routerLink: '/my-account/video-channels',
iconName: 'channel'
},
{
label: $localize`My videos`,
routerLink: '/my-account/videos',
iconName: 'videos',
isDisplayed: () => this.user.canSeeVideosLink
},
{
label: $localize`My playlists`,
routerLink: '/my-account/video-playlists',
iconName: 'playlists'
},
{
label: $localize`My subscriptions`,
routerLink: '/my-account/subscriptions',
iconName: 'subscriptions'
},
{
label: $localize`My history`,
routerLink: '/my-account/history/videos',
iconName: 'history'
}
]
}
if (this.isVideoImportEnabled()) {
libraryEntries.children.push({
label: 'My imports',
routerLink: '/my-account/video-imports',
iconName: 'cloud-download',
isDisplayed: () => this.user.canSeeVideosLink
})
}
const miscEntries: TopMenuDropdownParam = {
label: $localize`Misc`,
const moderationEntries: TopMenuDropdownParam = {
label: $localize`Moderation`,
children: [
{
label: $localize`Muted accounts`,
@ -98,29 +38,25 @@ export class MyAccountComponent implements OnInit {
iconName: 'peertube-x'
},
{
label: $localize`My abuse reports`,
label: $localize`Abuse reports`,
routerLink: '/my-account/abuses',
iconName: 'flag'
},
{
label: $localize`Ownership changes`,
routerLink: '/my-account/ownership',
iconName: 'download'
}
]
}
this.menuEntries = [
{
label: $localize`My settings`,
label: $localize`Settings`,
routerLink: '/my-account/settings'
},
{
label: $localize`My notifications`,
label: $localize`Notifications`,
routerLink: '/my-account/notifications'
},
libraryEntries,
miscEntries
moderationEntries
]
}
}

View File

@ -6,21 +6,14 @@ import { NgModule } from '@angular/core'
import { SharedAbuseListModule } from '@app/shared/shared-abuse-list'
import { SharedFormModule } from '@app/shared/shared-forms'
import { SharedGlobalIconModule } from '@app/shared/shared-icons'
import { SharedVideoLiveModule } from '@app/shared/shared-video-live'
import { SharedMainModule } from '@app/shared/shared-main'
import { SharedModerationModule } from '@app/shared/shared-moderation'
import { SharedShareModal } from '@app/shared/shared-share-modal'
import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings'
import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription/shared-user-subscription.module'
import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature'
import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist/shared-video-playlist.module'
import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component'
import { MyAccountBlocklistComponent } from './my-account-blocklist/my-account-blocklist.component'
import { MyAccountServerBlocklistComponent } from './my-account-blocklist/my-account-server-blocklist.component'
import { MyAccountHistoryComponent } from './my-account-history/my-account-history.component'
import { MyAccountNotificationsComponent } from './my-account-notifications/my-account-notifications.component'
import { MyAccountAcceptOwnershipComponent } from './my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component'
import { MyAccountOwnershipComponent } from './my-account-ownership/my-account-ownership.component'
import { MyAccountRoutingModule } from './my-account-routing.module'
import { MyAccountChangeEmailComponent } from './my-account-settings/my-account-change-email'
import { MyAccountChangePasswordComponent } from './my-account-settings/my-account-change-password/my-account-change-password.component'
@ -28,14 +21,6 @@ import { MyAccountDangerZoneComponent } from './my-account-settings/my-account-d
import { MyAccountNotificationPreferencesComponent } from './my-account-settings/my-account-notification-preferences'
import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component'
import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component'
import { MyAccountSubscriptionsComponent } from './my-account-subscriptions/my-account-subscriptions.component'
import { MyAccountVideoImportsComponent } from './my-account-video-imports/my-account-video-imports.component'
import { MyAccountVideoPlaylistCreateComponent } from './my-account-video-playlists/my-account-video-playlist-create.component'
import { MyAccountVideoPlaylistElementsComponent } from './my-account-video-playlists/my-account-video-playlist-elements.component'
import { MyAccountVideoPlaylistUpdateComponent } from './my-account-video-playlists/my-account-video-playlist-update.component'
import { MyAccountVideoPlaylistsComponent } from './my-account-video-playlists/my-account-video-playlists.component'
import { VideoChangeOwnershipComponent } from './my-account-videos/modals/video-change-ownership.component'
import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component'
import { MyAccountComponent } from './my-account.component'
@NgModule({
@ -50,14 +35,10 @@ import { MyAccountComponent } from './my-account.component'
SharedMainModule,
SharedFormModule,
SharedModerationModule,
SharedVideoMiniatureModule,
SharedUserSubscriptionModule,
SharedVideoPlaylistModule,
SharedUserInterfaceSettingsModule,
SharedGlobalIconModule,
SharedAbuseListModule,
SharedShareModal,
SharedVideoLiveModule
SharedShareModal
],
declarations: [
@ -67,26 +48,12 @@ import { MyAccountComponent } from './my-account.component'
MyAccountProfileComponent,
MyAccountChangeEmailComponent,
MyAccountVideosComponent,
VideoChangeOwnershipComponent,
MyAccountOwnershipComponent,
MyAccountAcceptOwnershipComponent,
MyAccountVideoImportsComponent,
MyAccountDangerZoneComponent,
MyAccountSubscriptionsComponent,
MyAccountBlocklistComponent,
MyAccountAbusesListComponent,
MyAccountServerBlocklistComponent,
MyAccountHistoryComponent,
MyAccountNotificationsComponent,
MyAccountNotificationPreferencesComponent,
MyAccountVideoPlaylistCreateComponent,
MyAccountVideoPlaylistUpdateComponent,
MyAccountVideoPlaylistsComponent,
MyAccountVideoPlaylistElementsComponent
MyAccountNotificationPreferencesComponent
],
exports: [

View File

@ -10,14 +10,13 @@ import {
import { FormValidatorService } from '@app/shared/shared-forms'
import { VideoChannelService } from '@app/shared/shared-main'
import { VideoChannelCreate } from '@shared/models'
import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
import { MyVideoChannelEdit } from './my-video-channel-edit'
@Component({
selector: 'my-account-video-channel-create',
templateUrl: './my-account-video-channel-edit.component.html',
styleUrls: [ './my-account-video-channel-edit.component.scss' ]
templateUrl: './my-video-channel-edit.component.html',
styleUrls: [ './my-video-channel-edit.component.scss' ]
})
export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelEdit implements OnInit {
export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements OnInit {
error: string
constructor (
@ -30,10 +29,6 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
super()
}
get instanceHost () {
return window.location.host
}
ngOnInit () {
this.buildForm({
name: VIDEO_CHANNEL_NAME_VALIDATOR,
@ -59,7 +54,7 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
this.authService.refreshUserInformation()
this.notifier.success($localize`Video channel ${videoChannelCreate.displayName} created.`)
this.router.navigate([ '/my-account', 'video-channels' ])
this.router.navigate([ '/my-library', 'video-channels' ])
},
err => {

View File

@ -1,7 +1,7 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a routerLink="/my-account/video-channels" i18n>My Channels</a>
<a routerLink="/my-library/video-channels" i18n>My Channels</a>
</li>
<ng-container *ngIf="isCreation()">
@ -10,7 +10,7 @@
<ng-container *ngIf="!isCreation()">
<li class="breadcrumb-item active" i18n>Edit</li>
<li class="breadcrumb-item active" aria-current="page">
<a *ngIf="videoChannelToUpdate" [routerLink]="[ '/my-account/video-channels/update', videoChannelToUpdate?.nameWithHost ]">{{ videoChannelToUpdate?.displayName }}</a>
<a *ngIf="videoChannelToUpdate" [routerLink]="[ '/my-library/video-channels/update', videoChannelToUpdate?.nameWithHost ]">{{ videoChannelToUpdate?.displayName }}</a>
</li>
</ng-container>
</ol>

View File

@ -1,14 +1,17 @@
import { FormReactive } from '@app/shared/shared-forms'
import { VideoChannel } from '@app/shared/shared-main'
export abstract class MyAccountVideoChannelEdit extends FormReactive {
export abstract class MyVideoChannelEdit extends FormReactive {
// We need it even in the create component because it's used in the edit template
videoChannelToUpdate: VideoChannel
instanceHost: string
abstract isCreation (): boolean
abstract getFormButtonTitle (): string
get instanceHost () {
return window.location.host
}
// We need this method so angular does not complain in child template that doesn't need this
onAvatarChange (formData: FormData) { /* empty */ }

View File

@ -10,14 +10,14 @@ import {
import { FormValidatorService } from '@app/shared/shared-forms'
import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
import { ServerConfig, VideoChannelUpdate } from '@shared/models'
import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
import { MyVideoChannelEdit } from './my-video-channel-edit'
@Component({
selector: 'my-account-video-channel-update',
templateUrl: './my-account-video-channel-edit.component.html',
styleUrls: [ './my-account-video-channel-edit.component.scss' ]
selector: 'my-video-channel-update',
templateUrl: './my-video-channel-edit.component.html',
styleUrls: [ './my-video-channel-edit.component.scss' ]
})
export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelEdit implements OnInit, OnDestroy {
export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements OnInit, OnDestroy {
error: string
videoChannelToUpdate: VideoChannel
@ -91,7 +91,7 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
this.notifier.success($localize`Video channel ${videoChannelUpdate.displayName} updated.`)
this.router.navigate([ '/my-account', 'video-channels' ])
this.router.navigate([ '/my-library', 'video-channels' ])
},
err => this.error = err.message

View File

@ -0,0 +1,41 @@
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
import { MyVideoChannelUpdateComponent } from './my-video-channel-update.component'
import { MyVideoChannelCreateComponent } from './my-video-channel-create.component'
import { MyVideoChannelsComponent } from './my-video-channels.component'
const myVideoChannelsRoutes: Routes = [
{
path: '',
component: MyVideoChannelsComponent,
data: {
meta: {
title: $localize`My video channels`
}
}
},
{
path: 'create',
component: MyVideoChannelCreateComponent,
data: {
meta: {
title: $localize`Create a new video channel`
}
}
},
{
path: 'update/:videoChannelId',
component: MyVideoChannelUpdateComponent,
data: {
meta: {
title: $localize`Update video channel`
}
}
}
]
@NgModule({
imports: [ RouterModule.forChild(myVideoChannelsRoutes) ],
exports: [ RouterModule ]
})
export class MyVideoChannelsRoutingModule {}

View File

@ -7,11 +7,10 @@ import { AuthService, ConfirmService, Notifier, ScreenService, User } from '@app
import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
@Component({
selector: 'my-account-video-channels',
templateUrl: './my-account-video-channels.component.html',
styleUrls: [ './my-account-video-channels.component.scss' ]
templateUrl: './my-video-channels.component.html',
styleUrls: [ './my-video-channels.component.scss' ]
})
export class MyAccountVideoChannelsComponent implements OnInit {
export class MyVideoChannelsComponent implements OnInit {
totalItems: number
videoChannels: VideoChannel[] = []

View File

@ -0,0 +1,31 @@
import { ChartModule } from 'primeng/chart'
import { NgModule } from '@angular/core'
import { SharedFormModule } from '@app/shared/shared-forms'
import { SharedGlobalIconModule } from '@app/shared/shared-icons'
import { SharedMainModule } from '@app/shared/shared-main'
import { MyVideoChannelCreateComponent } from './my-video-channel-create.component'
import { MyVideoChannelUpdateComponent } from './my-video-channel-update.component'
import { MyVideoChannelsRoutingModule } from './my-video-channels-routing.module'
import { MyVideoChannelsComponent } from './my-video-channels.component'
@NgModule({
imports: [
MyVideoChannelsRoutingModule,
ChartModule,
SharedMainModule,
SharedFormModule,
SharedGlobalIconModule
],
declarations: [
MyVideoChannelsComponent,
MyVideoChannelCreateComponent,
MyVideoChannelUpdateComponent
],
exports: [],
providers: []
})
export class MyVideoChannelsModule { }

View File

@ -0,0 +1,3 @@
export * from './my-library-routing.module'
export * from './my-library.component'
export * from './my-library.module'

View File

@ -15,11 +15,10 @@ import { UserHistoryService } from '@app/shared/shared-main'
import { AbstractVideoList } from '@app/shared/shared-video-miniature'
@Component({
selector: 'my-account-history',
templateUrl: './my-account-history.component.html',
styleUrls: [ './my-account-history.component.scss' ]
templateUrl: './my-history.component.html',
styleUrls: [ './my-history.component.scss' ]
})
export class MyAccountHistoryComponent extends AbstractVideoList implements OnInit, OnDestroy {
export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnDestroy {
titlePage: string
pagination: ComponentPagination = {
currentPage: 1,

View File

@ -0,0 +1,134 @@
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
import { MetaGuard } from '@ngx-meta/core'
import { LoginGuard } from '../core'
import { MyHistoryComponent } from './my-history/my-history.component'
import { MyLibraryComponent } from './my-library.component'
import { MyOwnershipComponent } from './my-ownership/my-ownership.component'
import { MySubscriptionsComponent } from './my-subscriptions/my-subscriptions.component'
import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component'
import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component'
import { MyVideoPlaylistElementsComponent } from './my-video-playlists/my-video-playlist-elements.component'
import { MyVideoPlaylistUpdateComponent } from './my-video-playlists/my-video-playlist-update.component'
import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component'
import { MyVideosComponent } from './my-videos/my-videos.component'
const myLibraryRoutes: Routes = [
{
path: '',
component: MyLibraryComponent,
canActivateChild: [ MetaGuard, LoginGuard ],
children: [
{
path: '',
redirectTo: 'video-channels',
pathMatch: 'full'
},
{
path: 'video-channels',
loadChildren: () => {
return import('./+my-video-channels/my-video-channels.module').then(m => m.MyVideoChannelsModule)
}
},
{
path: 'video-playlists',
component: MyVideoPlaylistsComponent,
data: {
meta: {
title: $localize`My playlists`
}
}
},
{
path: 'video-playlists/create',
component: MyVideoPlaylistCreateComponent,
data: {
meta: {
title: $localize`Create a new playlist`
}
}
},
{
path: 'video-playlists/:videoPlaylistId',
component: MyVideoPlaylistElementsComponent,
data: {
meta: {
title: $localize`Playlist elements`
}
}
},
{
path: 'video-playlists/update/:videoPlaylistId',
component: MyVideoPlaylistUpdateComponent,
data: {
meta: {
title: $localize`Update playlist`
}
}
},
{
path: 'videos',
component: MyVideosComponent,
data: {
meta: {
title: $localize`My videos`
},
reuse: {
enabled: true,
key: 'my-videos-list'
}
}
},
{
path: 'video-imports',
component: MyVideoImportsComponent,
data: {
meta: {
title: $localize`My video imports`
}
}
},
{
path: 'subscriptions',
component: MySubscriptionsComponent,
data: {
meta: {
title: $localize`My subscriptions`
}
}
},
{
path: 'ownership',
component: MyOwnershipComponent,
data: {
meta: {
title: $localize`Ownership changes`
}
}
},
{
path: 'history/videos',
component: MyHistoryComponent,
data: {
meta: {
title: $localize`My video history`
},
reuse: {
enabled: true,
key: 'my-videos-history-list'
}
}
}
]
}
]
@NgModule({
imports: [ RouterModule.forChild(myLibraryRoutes) ],
exports: [ RouterModule ]
})
export class MyLibraryRoutingModule {}

View File

@ -0,0 +1,7 @@
<div class="row">
<my-top-menu-dropdown [menuEntries]="menuEntries"></my-top-menu-dropdown>
<div class="margin-content pb-5" [ngClass]="{ 'offset-content': !isBroadcastMessageDisplayed }">
<router-outlet></router-outlet>
</div>
</div>

View File

@ -0,0 +1,13 @@
@import '_variables';
@import '_mixins';
.row {
flex-direction: column;
width: 100%;
& > my-top-menu-dropdown:nth-child(1) {
flex-grow: 1;
}
@include sub-menu-h1;
}

View File

@ -0,0 +1,76 @@
import { Component, OnInit } from '@angular/core'
import { AuthService, AuthUser, ScreenService, ServerService } from '@app/core'
import { ServerConfig } from '@shared/models'
import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdown.component'
@Component({
templateUrl: './my-library.component.html',
styleUrls: [ './my-library.component.scss' ]
})
export class MyLibraryComponent implements OnInit {
menuEntries: TopMenuDropdownParam[] = []
user: AuthUser
private serverConfig: ServerConfig
constructor (
private serverService: ServerService,
private authService: AuthService,
private screenService: ScreenService
) { }
get isBroadcastMessageDisplayed () {
return this.screenService.isBroadcastMessageDisplayed
}
ngOnInit (): void {
this.serverConfig = this.serverService.getTmpConfig()
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 () {
this.menuEntries = [
{
label: $localize`Channels`,
routerLink: '/my-library/video-channels'
}
]
if (this.user.canSeeVideosLink) {
this.menuEntries.push({
label: $localize`Videos`,
routerLink: '/my-library/videos'
})
}
this.menuEntries = this.menuEntries.concat([
{
label: $localize`Playlists`,
routerLink: '/my-library/video-playlists'
},
{
label: $localize`Subscriptions`,
routerLink: '/my-library/subscriptions'
},
{
label: $localize`History`,
routerLink: '/my-library/history/videos'
}
])
}
}

View File

@ -0,0 +1,79 @@
import { AutoCompleteModule } from 'primeng/autocomplete'
import { InputSwitchModule } from 'primeng/inputswitch'
import { TableModule } from 'primeng/table'
import { DragDropModule } from '@angular/cdk/drag-drop'
import { NgModule } from '@angular/core'
import { SharedAbuseListModule } from '@app/shared/shared-abuse-list'
import { SharedFormModule } from '@app/shared/shared-forms'
import { SharedGlobalIconModule } from '@app/shared/shared-icons'
import { SharedMainModule } from '@app/shared/shared-main'
import { SharedModerationModule } from '@app/shared/shared-moderation'
import { SharedShareModal } from '@app/shared/shared-share-modal'
import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings'
import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription/shared-user-subscription.module'
import { SharedVideoLiveModule } from '@app/shared/shared-video-live'
import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature'
import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist/shared-video-playlist.module'
import { MyHistoryComponent } from './my-history/my-history.component'
import { MyLibraryRoutingModule } from './my-library-routing.module'
import { MyLibraryComponent } from './my-library.component'
import { MyAcceptOwnershipComponent } from './my-ownership/my-accept-ownership/my-accept-ownership.component'
import { MyOwnershipComponent } from './my-ownership/my-ownership.component'
import { MySubscriptionsComponent } from './my-subscriptions/my-subscriptions.component'
import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component'
import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component'
import { MyVideoPlaylistElementsComponent } from './my-video-playlists/my-video-playlist-elements.component'
import { MyVideoPlaylistUpdateComponent } from './my-video-playlists/my-video-playlist-update.component'
import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component'
import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component'
import { MyVideosComponent } from './my-videos/my-videos.component'
@NgModule({
imports: [
MyLibraryRoutingModule,
AutoCompleteModule,
TableModule,
InputSwitchModule,
DragDropModule,
SharedMainModule,
SharedFormModule,
SharedModerationModule,
SharedVideoMiniatureModule,
SharedUserSubscriptionModule,
SharedVideoPlaylistModule,
SharedUserInterfaceSettingsModule,
SharedGlobalIconModule,
SharedAbuseListModule,
SharedShareModal,
SharedVideoLiveModule
],
declarations: [
MyLibraryComponent,
MyVideosComponent,
VideoChangeOwnershipComponent,
MyOwnershipComponent,
MyAcceptOwnershipComponent,
MyVideoImportsComponent,
MySubscriptionsComponent,
MyHistoryComponent,
MyVideoPlaylistCreateComponent,
MyVideoPlaylistUpdateComponent,
MyVideoPlaylistsComponent,
MyVideoPlaylistElementsComponent
],
exports: [
MyLibraryComponent
],
providers: []
})
export class MyLibraryModule {
}

View File

@ -7,11 +7,11 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { VideoChangeOwnership, VideoChannel } from '@shared/models'
@Component({
selector: 'my-account-accept-ownership',
templateUrl: './my-account-accept-ownership.component.html',
styleUrls: [ './my-account-accept-ownership.component.scss' ]
selector: 'my-accept-ownership',
templateUrl: './my-accept-ownership.component.html',
styleUrls: [ './my-accept-ownership.component.scss' ]
})
export class MyAccountAcceptOwnershipComponent extends FormReactive implements OnInit {
export class MyAcceptOwnershipComponent extends FormReactive implements OnInit {
@Output() accepted = new EventEmitter<void>()
@ViewChild('modal', { static: true }) modal: ElementRef

View File

@ -87,4 +87,4 @@
</ng-template>
</p-table>
<my-account-accept-ownership #myAccountAcceptOwnershipComponent (accepted)="accepted()"></my-account-accept-ownership>
<my-accept-ownership #myAcceptOwnershipComponent (accepted)="accepted()"></my-accept-ownership>

View File

@ -1,23 +1,21 @@
import { SortMeta } from 'primeng/api'
import { Component, OnInit, ViewChild } from '@angular/core'
import { Notifier, RestPagination, RestTable } from '@app/core'
import { VideoOwnershipService, Actor, Video, Account } from '@app/shared/shared-main'
import { Account, Actor, VideoOwnershipService } from '@app/shared/shared-main'
import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '@shared/models'
import { MyAccountAcceptOwnershipComponent } from './my-account-accept-ownership/my-account-accept-ownership.component'
import { getAbsoluteAPIUrl } from '@app/helpers'
import { MyAcceptOwnershipComponent } from './my-accept-ownership/my-accept-ownership.component'
@Component({
selector: 'my-account-ownership',
templateUrl: './my-account-ownership.component.html',
styleUrls: [ './my-account-ownership.component.scss' ]
templateUrl: './my-ownership.component.html',
styleUrls: [ './my-ownership.component.scss' ]
})
export class MyAccountOwnershipComponent extends RestTable implements OnInit {
export class MyOwnershipComponent extends RestTable implements OnInit {
videoChangeOwnerships: VideoChangeOwnership[] = []
totalRecords = 0
sort: SortMeta = { field: 'createdAt', order: -1 }
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
@ViewChild('myAccountAcceptOwnershipComponent', { static: true }) myAccountAcceptOwnershipComponent: MyAccountAcceptOwnershipComponent
@ViewChild('myAcceptOwnershipComponent', { static: true }) myAccountAcceptOwnershipComponent: MyAcceptOwnershipComponent
constructor (
private notifier: Notifier,
@ -31,7 +29,7 @@ export class MyAccountOwnershipComponent extends RestTable implements OnInit {
}
getIdentifier () {
return 'MyAccountOwnershipComponent'
return 'MyOwnershipComponent'
}
getStatusClass (status: VideoChangeOwnershipStatus) {

View File

@ -1,16 +1,15 @@
import { Subject } from 'rxjs'
import { debounceTime } from 'rxjs/operators'
import { Component, OnInit } from '@angular/core'
import { ComponentPagination, Notifier } from '@app/core'
import { VideoChannel } from '@app/shared/shared-main'
import { UserSubscriptionService } from '@app/shared/shared-user-subscription'
import { debounceTime } from 'rxjs/operators'
@Component({
selector: 'my-account-subscriptions',
templateUrl: './my-account-subscriptions.component.html',
styleUrls: [ './my-account-subscriptions.component.scss' ]
templateUrl: './my-subscriptions.component.html',
styleUrls: [ './my-subscriptions.component.scss' ]
})
export class MyAccountSubscriptionsComponent implements OnInit {
export class MySubscriptionsComponent implements OnInit {
videoChannels: VideoChannel[] = []
pagination: ComponentPagination = {

View File

@ -5,11 +5,10 @@ import { VideoImportService } from '@app/shared/shared-main'
import { VideoImport, VideoImportState } from '@shared/models'
@Component({
selector: 'my-account-video-imports',
templateUrl: './my-account-video-imports.component.html',
styleUrls: [ './my-account-video-imports.component.scss' ]
templateUrl: './my-video-imports.component.html',
styleUrls: [ './my-video-imports.component.scss' ]
})
export class MyAccountVideoImportsComponent extends RestTable implements OnInit {
export class MyVideoImportsComponent extends RestTable implements OnInit {
videoImports: VideoImport[] = []
totalRecords = 0
sort: SortMeta = { field: 'createdAt', order: 1 }
@ -27,7 +26,7 @@ export class MyAccountVideoImportsComponent extends RestTable implements OnInit
}
getIdentifier () {
return 'MyAccountVideoImportsComponent'
return 'MyVideoImportsComponent'
}
getVideoImportStateClass (state: VideoImportState) {

View File

@ -13,14 +13,13 @@ import { FormValidatorService } from '@app/shared/shared-forms'
import { VideoPlaylistService } from '@app/shared/shared-video-playlist'
import { VideoPlaylistCreate } from '@shared/models/videos/playlist/video-playlist-create.model'
import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model'
import { MyAccountVideoPlaylistEdit } from './my-account-video-playlist-edit'
import { MyVideoPlaylistEdit } from './my-video-playlist-edit'
@Component({
selector: 'my-account-video-playlist-create',
templateUrl: './my-account-video-playlist-edit.component.html',
styleUrls: [ './my-account-video-playlist-edit.component.scss' ]
templateUrl: './my-video-playlist-edit.component.html',
styleUrls: [ './my-video-playlist-edit.component.scss' ]
})
export class MyAccountVideoPlaylistCreateComponent extends MyAccountVideoPlaylistEdit implements OnInit {
export class MyVideoPlaylistCreateComponent extends MyVideoPlaylistEdit implements OnInit {
error: string
constructor (
@ -75,7 +74,7 @@ export class MyAccountVideoPlaylistCreateComponent extends MyAccountVideoPlaylis
this.videoPlaylistService.createVideoPlaylist(videoPlaylistCreate).subscribe(
() => {
this.notifier.success($localize`Playlist ${videoPlaylistCreate.displayName} created.`)
this.router.navigate([ '/my-account', 'video-playlists' ])
this.router.navigate([ '/my-library', 'video-playlists' ])
},
err => this.error = err.message

View File

@ -1,7 +1,7 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a routerLink="/my-account/video-playlists" i18n>My Playlists</a>
<a routerLink="/my-library/video-playlists" i18n>My Playlists</a>
</li>
<ng-container *ngIf="isCreation()">
@ -10,7 +10,7 @@
<ng-container *ngIf="!isCreation()">
<li class="breadcrumb-item active" i18n>Edit</li>
<li class="breadcrumb-item active" aria-current="page">
<a *ngIf="videoPlaylistToUpdate" [routerLink]="[ '/my-account/video-playlists/update', videoPlaylistToUpdate?.uuid ]">{{ videoPlaylistToUpdate?.displayName }}</a>
<a *ngIf="videoPlaylistToUpdate" [routerLink]="[ '/my-library/video-playlists/update', videoPlaylistToUpdate?.uuid ]">{{ videoPlaylistToUpdate?.displayName }}</a>
</li>
</ng-container>
</ol>

View File

@ -2,7 +2,7 @@ import { FormReactive, SelectChannelItem } from '@app/shared/shared-forms'
import { VideoConstant, VideoPlaylistPrivacy } from '@shared/models'
import { VideoPlaylist } from '@shared/models/videos/playlist/video-playlist.model'
export abstract class MyAccountVideoPlaylistEdit extends FormReactive {
export abstract class MyVideoPlaylistEdit extends FormReactive {
// Declare it here to avoid errors in create template
videoPlaylistToUpdate: VideoPlaylist
userVideoChannels: SelectChannelItem[] = []

View File

@ -9,11 +9,10 @@ import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/
import { VideoPlaylistType } from '@shared/models'
@Component({
selector: 'my-account-video-playlist-elements',
templateUrl: './my-account-video-playlist-elements.component.html',
styleUrls: [ './my-account-video-playlist-elements.component.scss' ]
templateUrl: './my-video-playlist-elements.component.html',
styleUrls: [ './my-video-playlist-elements.component.scss' ]
})
export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestroy {
export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
@ViewChild('videoShareModal') videoShareModal: VideoShareComponent
playlistElements: VideoPlaylistElement[] = []
@ -47,7 +46,7 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
{
label: $localize`Update playlist`,
iconName: 'edit',
linkBuilder: playlist => [ '/my-account', 'video-playlists', 'update', playlist.uuid ]
linkBuilder: playlist => [ '/my-library', 'video-playlists', 'update', playlist.uuid ]
},
{
label: $localize`Delete playlist`,
@ -132,7 +131,7 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
this.videoPlaylistService.removeVideoPlaylist(videoPlaylist)
.subscribe(
() => {
this.router.navigate([ '/my-account', 'video-playlists' ])
this.router.navigate([ '/my-library', 'video-playlists' ])
this.notifier.success($localize`Playlist ${videoPlaylist.displayName} deleted.`)
},

View File

@ -14,14 +14,13 @@ import {
import { FormValidatorService } from '@app/shared/shared-forms'
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
import { VideoPlaylistUpdate } from '@shared/models'
import { MyAccountVideoPlaylistEdit } from './my-account-video-playlist-edit'
import { MyVideoPlaylistEdit } from './my-video-playlist-edit'
@Component({
selector: 'my-account-video-playlist-update',
templateUrl: './my-account-video-playlist-edit.component.html',
styleUrls: [ './my-account-video-playlist-edit.component.scss' ]
templateUrl: './my-video-playlist-edit.component.html',
styleUrls: [ './my-video-playlist-edit.component.scss' ]
})
export class MyAccountVideoPlaylistUpdateComponent extends MyAccountVideoPlaylistEdit implements OnInit, OnDestroy {
export class MyVideoPlaylistUpdateComponent extends MyVideoPlaylistEdit implements OnInit, OnDestroy {
error: string
videoPlaylistToUpdate: VideoPlaylist
@ -96,7 +95,7 @@ export class MyAccountVideoPlaylistUpdateComponent extends MyAccountVideoPlaylis
this.videoPlaylistService.updateVideoPlaylist(this.videoPlaylistToUpdate, videoPlaylistUpdate).subscribe(
() => {
this.notifier.success($localize`Playlist ${videoPlaylistUpdate.displayName} updated.`)
this.router.navigate([ '/my-account', 'video-playlists' ])
this.router.navigate([ '/my-library', 'video-playlists' ])
},
err => this.error = err.message

View File

@ -6,11 +6,10 @@ import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-pl
import { VideoPlaylistType } from '@shared/models'
@Component({
selector: 'my-account-video-playlists',
templateUrl: './my-account-video-playlists.component.html',
styleUrls: [ './my-account-video-playlists.component.scss' ]
templateUrl: './my-video-playlists.component.html',
styleUrls: [ './my-video-playlists.component.scss' ]
})
export class MyAccountVideoPlaylistsComponent implements OnInit {
export class MyVideoPlaylistsComponent implements OnInit {
videoPlaylistsSearch: string
videoPlaylists: VideoPlaylist[] = []
videoPlaylistSearchChanged = new Subject<string>()

View File

@ -4,6 +4,18 @@
<ng-container i18n>My videos</ng-container>
<span class="badge badge-secondary"> {{ pagination.totalItems }}</span>
</span>
<div>
<a routerLink="/my-library/video-imports" class="button-link">
<my-global-icon iconName="cloud-download" aria-hidden="true"></my-global-icon>
<ng-container i18n>My imports</ng-container>
</a>
<a routerLink="/my-library/ownership" class="button-link">
<my-global-icon iconName="users" aria-hidden="true"></my-global-icon>
<ng-container i18n>Ownership changes</ng-container>
</a>
</div>
</h1>
<div class="videos-header d-flex justify-content-between">

View File

@ -5,6 +5,21 @@ input[type=text] {
@include peertube-input-text(300px);
}
h1 {
display: flex;
justify-content: space-between;
.button-link {
@include peertube-button-link;
@include grey-button;
@include button-with-icon(18px, 3px, -1px);
&:not(:last-child) {
margin-right: 10px;
}
}
}
.action-button-delete-selection {
display: inline-block;
@ -50,18 +65,40 @@ input[type=text] {
align-self: flex-end;
}
my-delete-button,
my-edit-button {
margin-right: 10px;
}
@media screen and (max-width: $small-view) {
h1 {
flex-direction: column;
> span,
.button-link {
margin-bottom: 10px;
}
}
.action-button {
flex-direction: column;
align-self: center;
align-items: center;
margin-left: 0px;
}
my-edit-button {
margin: 15px 0 5px 0;
width: 100%;
text-align: center;
::ng-deep {
.action-button {
/* same width than a.video-thumbnail */
width: $video-thumbnail-width;
}
}
}
::ng-deep {
.video-miniature {
align-items: center;
@ -73,32 +110,6 @@ my-edit-button {
}
}
}
my-delete-button,
my-edit-button {
margin-right: 0px;
::ng-deep {
span, a {
margin-right: 0px;
}
}
}
my-delete-button,
my-edit-button,
my-button {
margin-top: 15px;
width: 100%;
text-align: center;
::ng-deep {
.action-button {
/* same width than a.video-thumbnail */
width: $video-thumbnail-width;
}
}
}
}
// Adapt my-video-miniature on small screens with menu

View File

@ -12,11 +12,10 @@ import { VideoSortField } from '@shared/models'
import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.component'
@Component({
selector: 'my-account-videos',
templateUrl: './my-account-videos.component.html',
styleUrls: [ './my-account-videos.component.scss' ]
templateUrl: './my-videos.component.html',
styleUrls: [ './my-videos.component.scss' ]
})
export class MyAccountVideosComponent implements OnInit, DisableForReuseHook {
export class MyVideosComponent implements OnInit, DisableForReuseHook {
@ViewChild('videosSelection', { static: true }) videosSelection: VideosSelectionComponent
@ViewChild('videoChangeOwnershipModal', { static: true }) videoChangeOwnershipModal: VideoChangeOwnershipComponent
@ViewChild('liveStreamInformationModal', { static: true }) liveStreamInformationModal: LiveStreamInformationComponent

View File

@ -18,7 +18,7 @@
</div>
<div class="right-buttons">
<a *ngIf="isChannelManageable && !isInSmallView" [routerLink]="[ '/my-account/video-channels/update', videoChannel.nameWithHost ]" class="btn btn-outline-tertiary mr-2" i18n>
<a *ngIf="isChannelManageable && !isInSmallView" [routerLink]="[ '/my-library/video-channels/update', videoChannel.nameWithHost ]" class="btn btn-outline-tertiary mr-2" i18n>
Manage channel
</a>
<my-subscribe-button #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button>

View File

@ -128,7 +128,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
this.isUpdatingVideo = false
this.notifier.success($localize`Video to import updated.`)
this.router.navigate([ '/my-account', 'video-imports' ])
this.router.navigate([ '/my-library', 'video-imports' ])
},
err => {

View File

@ -138,7 +138,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
this.isUpdatingVideo = false
this.notifier.success($localize`Video to import updated.`)
this.router.navigate([ '/my-account', 'video-imports' ])
this.router.navigate([ '/my-library', 'video-imports' ])
},
err => {

View File

@ -34,7 +34,7 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement
this.titlePage = $localize`Videos from your subscriptions`
this.actions.push({
routerLink: '/my-account/subscriptions',
routerLink: '/my-library/subscriptions',
label: $localize`Subscriptions`,
iconName: 'cog'
})

View File

@ -17,6 +17,10 @@ const routes: Routes = [
path: 'my-account',
loadChildren: () => import('./+my-account/my-account.module').then(m => m.MyAccountModule)
},
{
path: 'my-library',
loadChildren: () => import('./+my-library/my-library.module').then(m => m.MyLibraryModule)
},
{
path: 'verify-account',
loadChildren: () => import('./+signup/+verify-account/verify-account.module').then(m => m.VerifyAccountModule)

View File

@ -62,15 +62,15 @@ export class AuthService {
return false
}, undefined, $localize`Go to my subscriptions`),
new Hotkey('m v', (event: KeyboardEvent): boolean => {
this.router.navigate([ '/my-account/videos' ])
this.router.navigate([ '/my-library/videos' ])
return false
}, undefined, $localize`Go to my videos`),
new Hotkey('m i', (event: KeyboardEvent): boolean => {
this.router.navigate([ '/my-account/video-imports' ])
this.router.navigate([ '/my-library/video-imports' ])
return false
}, undefined, $localize`Go to my imports`),
new Hotkey('m c', (event: KeyboardEvent): boolean => {
this.router.navigate([ '/my-account/video-channels' ])
this.router.navigate([ '/my-library/video-channels' ])
return false
}, undefined, $localize`Go to my channels`)
]

View File

@ -1,98 +1,106 @@
<div class="menu-wrapper">
<menu [ngClass]="{ 'logged-in': isLoggedIn }">
<menu [ngClass]="{ 'is-logged-in': isLoggedIn }">
<div class="top-menu">
<div *ngIf="isLoggedIn" class="logged-in-block">
<my-avatar-notification [user]="user" (navigate)="onSameUrlRestoreScrollPosition($event)"></my-avatar-notification>
<div>
<my-avatar-notification [user]="user" (navigate)="onSameUrlRestoreScrollPosition($event)"></my-avatar-notification>
<div class="logged-in-info">
<a *ngIf="user.account" [routerLink]="[ '/accounts', user.account.nameWithHost ]" class="logged-in-display-name">{{ user.account?.displayName }}</a>
<a *ngIf="!user.account" routerLink="/my-account/settings" class="logged-in-display-name">{{ user.account?.displayName }}</a>
<div class="logged-in-info">
<a *ngIf="user.account" [routerLink]="[ '/accounts', user.account.nameWithHost ]" class="logged-in-display-name">{{ user.account?.displayName }}</a>
<a *ngIf="!user.account" routerLink="/my-account/settings" class="logged-in-display-name">{{ user.account?.displayName }}</a>
<div class="logged-in-username">{{ user.username }}</div>
<div class="logged-in-username">@{{ user.username }}</div>
</div>
<div class="logged-in-more" ngbDropdown [placement]="placement" container="body" autoClose="outside">
<my-global-icon iconName="more-vertical" ngbDropdownToggle role="button"></my-global-icon>
<div ngbDropdownMenu>
<a *ngIf="user.account" ngbDropdownItem ngbDropdownToggle class="dropdown-item" [routerLink]="[ '/accounts', user.account.nameWithHost ]">
<my-global-icon iconName="go" aria-hidden="true"></my-global-icon> <ng-container i18n>Public profile</ng-container>
</a>
<div class="dropdown-divider"></div>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/notifications">
<my-global-icon iconName="inbox-full" aria-hidden="true"></my-global-icon> <ng-container i18n>My notifications</ng-container>
</a>
<div class="dropdown-divider"></div>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openLanguageChooser()">
<my-global-icon iconName="language" aria-hidden="true"></my-global-icon>
<span i18n>Interface:</span>
<span class="ml-auto text-muted">{{ language }}</span>
</a>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="video-languages-subtitles"
#settingsLanguagesSubtitles (click)="onSameUrlRestoreScrollPosition(settingsLanguagesSubtitles)">
<my-global-icon iconName="video-lang" aria-hidden="true"></my-global-icon>
<span i18n>Videos:</span>
<span class="ml-auto text-muted">{{ videoLanguages.join(', ') }}</span>
</a>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings"
fragment="video-sensitive-content-policy" #settingsSensitiveContentPolicy
(click)="onSameUrlRestoreScrollPosition(settingsSensitiveContentPolicy)">
<my-global-icon class="hover-display-toggle" [ngClass]="{ 'not-displayed': user.nsfwPolicy === 'display' }" iconName="sensitive" aria-hidden="true"></my-global-icon>
<my-global-icon class="hover-display-toggle" [ngClass]="{ 'not-displayed': user.nsfwPolicy !== 'display' }" iconName="unsensitive" aria-hidden="true"></my-global-icon>
<span i18n>Sensitive:</span>
<span class="ml-auto text-muted">{{ nsfwPolicy }}</span>
</a>
<a ngbDropdownItem class="dropdown-item" (click)="toggleUseP2P()">
<my-global-icon iconName="p2p" aria-hidden="true"></my-global-icon>
<ng-container i18n>Help share videos</ng-container>
<input type="checkbox" [checked]="user.webTorrentEnabled"/><label class="ml-auto" for="switch">Toggle p2p</label>
</a>
<div class="dropdown-divider"></div>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openHotkeysCheatSheet()">
<i class="icon icon-shortcuts" aria-hidden="true"></i> <ng-container i18n>Keyboard shortcuts</ng-container>
</a>
<a ngbDropdownItem ngbDropdownToggle (click)="logout($event)" class="dropdown-item" href="#">
<my-global-icon iconName="sign-out" aria-hidden="true"></my-global-icon> <ng-container i18n>Log out</ng-container>
</a>
</div>
</div>
</div>
<div class="logged-in-more" ngbDropdown [placement]="placement" container="body" autoClose="outside">
<my-global-icon iconName="more-vertical" ngbDropdownToggle role="button"></my-global-icon>
<div class="logged-in-menu">
<a routerLink="/my-account" routerLinkActive="active" #settingsLink (click)="onSameUrlRestoreScrollPosition(settingsLink)">
<my-global-icon iconName="user" aria-hidden="true"></my-global-icon>
<ng-container i18n>My account</ng-container>
</a>
<div ngbDropdownMenu>
<a *ngIf="user.account" ngbDropdownItem ngbDropdownToggle class="dropdown-item" [routerLink]="[ '/accounts', user.account.nameWithHost ]">
<my-global-icon iconName="go" aria-hidden="true"></my-global-icon> <ng-container i18n>Public profile</ng-container>
</a>
<a routerLink="/my-library" routerLinkActive="active" #libraryLink (click)="onSameUrlRestoreScrollPosition(libraryLink)">
<my-global-icon iconName="channel" aria-hidden="true"></my-global-icon>
<ng-container i18n>My library</ng-container>
</a>
<div class="dropdown-divider"></div>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings"
#settingsLink (click)="onSameUrlRestoreScrollPosition(settingsLink)">
<my-global-icon iconName="user" aria-hidden="true"></my-global-icon> <ng-container i18n>Account settings</ng-container>
</a>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/video-channels"
#channelsLink (click)="onSameUrlRestoreScrollPosition(channelsLink)">
<my-global-icon iconName="channel" aria-hidden="true"></my-global-icon> <ng-container i18n>Channels settings</ng-container>
</a>
<div class="dropdown-divider"></div>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openLanguageChooser()">
<my-global-icon iconName="language" aria-hidden="true"></my-global-icon>
<span i18n>Interface:</span>
<span class="ml-auto text-muted">{{ language }}</span>
</a>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="video-languages-subtitles"
#settingsLanguagesSubtitles (click)="onSameUrlRestoreScrollPosition(settingsLanguagesSubtitles)">
<my-global-icon iconName="video-lang" aria-hidden="true"></my-global-icon>
<span i18n>Videos:</span>
<span class="ml-auto text-muted">{{ videoLanguages.join(', ') }}</span>
</a>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings"
fragment="video-sensitive-content-policy" #settingsSensitiveContentPolicy
(click)="onSameUrlRestoreScrollPosition(settingsSensitiveContentPolicy)">
<my-global-icon class="hover-display-toggle" [ngClass]="{ 'not-displayed': user.nsfwPolicy === 'display' }" iconName="sensitive" aria-hidden="true"></my-global-icon>
<my-global-icon class="hover-display-toggle" [ngClass]="{ 'not-displayed': user.nsfwPolicy !== 'display' }" iconName="unsensitive" aria-hidden="true"></my-global-icon>
<span i18n>Sensitive:</span>
<span class="ml-auto text-muted">{{ nsfwPolicy }}</span>
</a>
<a ngbDropdownItem class="dropdown-item" (click)="toggleUseP2P()">
<my-global-icon iconName="p2p" aria-hidden="true"></my-global-icon>
<ng-container i18n>Help share videos</ng-container>
<input type="checkbox" [checked]="user.webTorrentEnabled"/><label class="ml-auto" for="switch">Toggle p2p</label>
</a>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings"
#settingsMoreLink (click)="onSameUrlRestoreScrollPosition(settingsMoreLink)">
<my-global-icon iconName="more-horizontal" aria-hidden="true"></my-global-icon> <ng-container i18n>More account settings</ng-container>
</a>
<div class="dropdown-divider"></div>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openHotkeysCheatSheet()">
<i class="icon icon-shortcuts" aria-hidden="true"></i> <ng-container i18n>Keyboard shortcuts</ng-container>
</a>
<a ngbDropdownItem ngbDropdownToggle (click)="logout($event)" class="dropdown-item" href="#">
<my-global-icon iconName="sign-out" aria-hidden="true"></my-global-icon> <ng-container i18n>Log out</ng-container>
</a>
</div>
<a *ngIf="userHasAdminAccess" [routerLink]="getFirstAdminRouteAvailable()" routerLinkActive="active">
<my-global-icon iconName="cog" aria-hidden="true"></my-global-icon>
<ng-container i18n>Administration</ng-container>
</a>
</div>
</div>
<div *ngIf="!isLoggedIn" class="button-block">
<div *ngIf="!isLoggedIn" class="login-buttons-block">
<a i18n routerLink="/login" class="login-button">Login</a>
<a i18n *ngIf="isRegistrationAllowed()" routerLink="/signup" class="create-account-button">Create an account</a>
</div>
<div *ngIf="isLoggedIn" class="panel-block">
<div i18n class="block-title">MY LIBRARY</div>
<div *ngIf="isLoggedIn" class="in-my-account">
<div i18n class="block-title">IN MY ACCOUNT</div>
<a *ngIf="user.canSeeVideosLink" routerLink="/my-account/videos" routerLinkActive="active">
<a *ngIf="user.canSeeVideosLink" routerLink="/my-library/videos" routerLinkActive="active">
<my-global-icon iconName="videos" aria-hidden="true"></my-global-icon>
<ng-container i18n>Videos</ng-container>
</a>
<a routerLink="/my-account/video-playlists" routerLinkActive="active">
<a routerLink="/my-library/video-playlists" routerLinkActive="active">
<my-global-icon iconName="playlists" aria-hidden="true"></my-global-icon>
<ng-container i18n>Playlists</ng-container>
</a>
@ -102,15 +110,15 @@
<ng-container i18n>Subscriptions</ng-container>
</a>
<a routerLink="/my-account/history/videos" routerLinkActive="active">
<a routerLink="/my-library/history/videos" routerLinkActive="active">
<my-global-icon iconName="history" aria-hidden="true"></my-global-icon>
<ng-container i18n>History</ng-container>
</a>
</div>
<div class="panel-block">
<div i18n class="block-title">VIDEOS</div>
<div class="on-instance">
<div i18n class="block-title">ON {{instanceName}}</div>
<a routerLink="/videos/overview" routerLinkActive="active">
<my-global-icon iconName="globe" aria-hidden="true"></my-global-icon>
@ -134,22 +142,19 @@
<a routerLink="/videos/local" routerLinkActive="active">
<my-global-icon iconName="home" aria-hidden="true"></my-global-icon>
<ng-container i18n>Local</ng-container>
<ng-container i18n>Local videos</ng-container>
</a>
</div>
</div>
<div class="footer">
<div class="panel-block">
<a *ngIf="userHasAdminAccess" [routerLink]="getFirstAdminRouteAvailable()" routerLinkActive="active">
<my-global-icon iconName="cog" aria-hidden="true"></my-global-icon>
<ng-container i18n>Administration</ng-container>
</a>
<div class="footer-block">
<a *ngIf="!isLoggedIn" (click)="openQuickSettings()">
<my-global-icon iconName="cog" aria-hidden="true"></my-global-icon>
<ng-container i18n>Settings</ng-container>
</a>
<a routerLink="/about/instance">
<a routerLink="/about" routerLinkActive="active">
<my-global-icon iconName="help" aria-hidden="true"></my-global-icon>
<ng-container i18n>About</ng-container>
</a>
@ -168,7 +173,7 @@
<a i18n href="https://joinpeertube.org/faq" i18n-title title="Frequently asked questions about PeerTube" target="_blank" rel="noopener noreferrer">FAQ</a>
<a i18n routerLink="/about/instance" fragment="statistics">Stats</a>
<a i18n href="https://docs.joinpeertube.org/api-rest-reference.html" i18n-title title="API documentation" target="_blank" rel="noopener noreferrer">API</a>
<a (click)="openHotkeysCheatSheet()" class="c-hand" i18n>Shortcuts</a>
<a (click)="openHotkeysCheatSheet()" class="c-hand" i18n>Keyboard shortcuts</a>
</div>
</div>

View File

@ -1,6 +1,39 @@
@import '_variables';
@import '_mixins';
$menu-link-icon-size: 22px;
$menu-link-icon-margin-right: 18px;
@mixin menu-link {
display: flex;
align-items: center;
padding-left: $menu-lateral-padding;
color: pvar(--menuForegroundColor);
cursor: pointer;
font-size: 16px;
white-space: normal;
word-break: break-word;
padding-right: 20px;
transition: background-color .1s ease-in-out;
&.active {
background-color: rgba(255, 255, 255, 0.15);
}
&:hover, &.focus-visible {
background-color: rgba(255, 255, 255, 0.10);
}
my-global-icon {
@include apply-svg-color(#808080);
display: flex;
width: $menu-link-icon-size;
height: $menu-link-icon-size;
margin-right: $menu-link-icon-margin-right;
}
}
.menu-wrapper {
position: fixed;
height: calc(100vh - #{$header-height});
@ -14,14 +47,14 @@ menu {
@include ellipsis;
background-color: pvar(--menuBackgroundColor);
margin: 0;
padding: 0;
height: 100%;
overflow-x: hidden;
color: pvar(--menuForegroundColor);
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
margin: 0;
padding: 0;
&:focus, &:hover {
overflow-y: auto;
@ -31,7 +64,7 @@ menu {
overflow-y: auto;
}
&.logged-in {
&.is-logged-in {
.panel-block {
margin-bottom: 20px;
}
@ -40,19 +73,22 @@ menu {
margin-bottom: 15px;
}
}
}
.top-menu {
flex-grow: 1;
width: $menu-width;
}
.top-menu {
flex-grow: 1;
width: $menu-width;
}
.logged-in-block {
height: 100px;
background-color: rgba(255, 255, 255, 0.15);
.logged-in-block {
margin-bottom: 20px;
background-color: rgba(255, 255, 255, 0.15);
> div:first-child {
height: 80px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
.logged-in-info {
@include ellipsis;
@ -95,32 +131,88 @@ menu {
}
}
.button-block {
margin: 30px 25px 35px 25px;
.logged-in-menu {
display: flex;
flex-direction: column;
align-items: flex-start;
border-top: 1px solid var(--greyForegroundColor);
.login-button {
@include peertube-button-link;
@include orange-button;
a {
@include menu-link;
@include disable-default-a-behaviour;
display: block;
$icon-size: 13px;
$additional-margin: ($menu-link-icon-size - $icon-size) / 2;
font-size: 14px;
width: 100%;
margin-bottom: 10px;
}
min-height: 35px;
.create-account-button {
@include peertube-button-link;
my-global-icon {
width: $icon-size;
height: $icon-size;
display: block;
width: 100%;
// Keep aligned with other icons
margin-left: $additional-margin;
color: #fff;
background-color: rgba(255, 255, 255, 0.25);
&[iconName="channel"] {
margin-top: -2px;
}
}
&:hover {
background-color: rgba(255, 255, 255, 0.28);
&.active,
&:hover,
&:focus-visible {
my-global-icon {
@include apply-svg-color(var(--mainBackgroundColor));
}
}
&.active {
$border-left-width: 4px;
font-weight: $font-semibold;
border-left: $border-left-width solid var(--mainColor);
my-global-icon {
margin-left: $additional-margin - $border-left-width;
}
}
}
}
}
.login-buttons-block {
margin: 30px 25px 35px 25px;
.login-button {
@include peertube-button-link;
@include orange-button;
display: block;
width: 100%;
margin-bottom: 10px;
}
.create-account-button {
@include peertube-button-link;
display: block;
width: 100%;
color: #fff;
background-color: rgba(255, 255, 255, 0.25);
&:hover {
background-color: rgba(255, 255, 255, 0.28);
}
}
}
.in-my-account,
.on-instance,
.footer-block {
margin-bottom: 15px;
.block-title {
text-transform: uppercase;
@ -128,117 +220,87 @@ menu {
font-size: 13px;
margin-bottom: 25px;
margin-left: 26px;
@include ellipsis;
margin-right: 30px;
}
.panel-block {
margin-bottom: 15px;
a {
@include menu-link;
@include disable-default-a-behaviour;
a {
@include disable-default-a-behaviour;
min-height: 40px;
display: flex;
align-items: center;
padding-left: $menu-lateral-padding;
color: pvar(--menuForegroundColor);
cursor: pointer;
min-height: 40px;
font-size: 16px;
transition: background-color .1s ease-in-out;
white-space: normal;
word-break: break-word;
padding-right: 20px;
my-global-icon {
&[iconName="playlists"] {
height: 24px;
width: 24px;
&.active {
background-color: rgba(255, 255, 255, 0.15);
margin-right: 16px;
}
&:hover, &.focus-visible {
background-color: rgba(255, 255, 255, 0.10);
}
my-global-icon {
@include apply-svg-color(#808080);
display: flex;
width: 22px;
height: 22px;
margin-right: 18px;
&[iconName="playlists"] {
height: 24px;
width: 24px;
margin-right: 16px;
}
&[iconName="videos"] {
position: relative;
right: -1px;
}
}
.icon {
@include icon(22px);
margin-right: 18px;
&[iconName="videos"] {
position: relative;
right: -1px;
}
}
}
}
.footer {
width: $menu-width;
padding-bottom: 15px;
.footer {
width: $menu-width;
padding-bottom: 15px;
.bottom-links {
.bottom-links {
display: flex;
flex-direction: column;
padding: 0 $menu-lateral-padding;
}
$footer-links-base-opacity: .8;
.footer-links {
&, > div {
display: flex;
flex-direction: column;
padding: 0 $menu-lateral-padding;
flex-wrap: wrap;
}
$footer-links-base-opacity: .8;
.footer-links {
&, > div {
display: flex;
flex-wrap: wrap;
}
a, span[role=button] {
display: inline-block;
text-decoration: none;
color: pvar(--mainBackgroundColor);
opacity: $footer-links-base-opacity;
white-space: nowrap;
font-size: 90%;
font-weight: 500;
line-height: 1.4rem;
margin-right: 8px;
&.inline-global-icon {
display: inline-flex;
align-items: center;
white-space: nowrap;
height: 1.4rem;
my-global-icon {
@include apply-svg-color(pvar(--mainBackgroundColor));
display: flex;
width: auto;
height: 90%;
margin-right: .2rem;
}
}
}
}
.footer-copyleft small a {
@include disable-default-a-behaviour;
a, span[role=button] {
display: inline-block;
text-decoration: none;
color: pvar(--mainBackgroundColor);
opacity: $footer-links-base-opacity - .2;
opacity: $footer-links-base-opacity;
white-space: nowrap;
font-size: 90%;
font-weight: 500;
line-height: 1.4rem;
margin-right: 8px;
&.inline-global-icon {
display: inline-flex;
align-items: center;
white-space: nowrap;
height: 1.4rem;
my-global-icon {
@include apply-svg-color(pvar(--mainBackgroundColor));
display: flex;
width: auto;
height: 90%;
margin-right: .2rem;
}
}
}
}
.footer-copyleft small a {
@include disable-default-a-behaviour;
color: pvar(--mainBackgroundColor);
opacity: $footer-links-base-opacity - .2;
}
}
.dropdown {
@ -292,7 +354,7 @@ menu {
.icon {
@include disable-outline;
@include icon(22px);
@include icon($menu-link-icon-size);
opacity: 0.8;
&.icon-shortcuts {

View File

@ -61,6 +61,29 @@ export class MenuComponent implements OnInit {
}
}
get language () {
return this.languageChooserModal.getCurrentLanguage()
}
get nsfwPolicy () {
if (!this.user) return
switch (this.user.nsfwPolicy) {
case 'do_not_list':
return $localize`hide`
case 'blur':
return $localize`blur`
case 'display':
return $localize`display`
}
}
get instanceName () {
return this.serverConfig.instance.name
}
ngOnInit () {
this.serverConfig = this.serverService.getTmpConfig()
this.serverService.getConfig()
@ -109,25 +132,6 @@ export class MenuComponent implements OnInit {
})
}
get language () {
return this.languageChooserModal.getCurrentLanguage()
}
get nsfwPolicy () {
if (!this.user) return
switch (this.user.nsfwPolicy) {
case 'do_not_list':
return $localize`hide`
case 'blur':
return $localize`blur`
case 'display':
return $localize`display`
}
}
isRegistrationAllowed () {
return this.serverConfig.signup.allowed &&
this.serverConfig.signup.allowedForCurrentIP

View File

@ -211,7 +211,7 @@ export class UserNotification implements UserNotificationServer {
}
private buildVideoImportUrl () {
return '/my-account/video-imports'
return '/my-library/video-imports'
}
private buildVideoImportIdentifier (videoImport: { targetUrl?: string, magnetUri?: string, torrentName?: string }) {

View File

@ -36,7 +36,7 @@
</div>
</a>
<my-edit-button *ngIf="owned && touchScreenEditButton" [routerLink]="[ '/my-account', 'video-playlists', playlist.uuid ]"></my-edit-button>
<my-edit-button *ngIf="owned && touchScreenEditButton" [routerLink]="[ '/my-library', 'video-playlists', playlist.uuid ]"></my-edit-button>
<div *ngIf="owned" class="more" ngbDropdown #moreDropdown="ngbDropdown" placement="left auto"
(openChange)="onDropdownOpenChange()" autoClose="outside"

View File

@ -14,7 +14,7 @@ export class VideoPlaylistMiniatureComponent {
@Input() displayPrivacy = false
getPlaylistUrl () {
if (this.toManage) return [ '/my-account/video-playlists', this.playlist.uuid ]
if (this.toManage) return [ '/my-library/video-playlists', this.playlist.uuid ]
if (this.playlist.videosLength === 0) return null
return [ '/videos/watch/playlist', this.playlist.uuid ]

View File

@ -14,13 +14,11 @@ $grey-foreground-color: #585858;
$grey-foreground-hover-color: #303030;
$grey-button-outline-color: scale-color($grey-foreground-color, $alpha: -95%);
// Palette
$main-color: hsl(24, 90%, 50%);
$main-hover-color: lighten($main-color, 5%);
$main-color-lighter: lighten($main-color, 10%);
$main-color-lightest: lighten($main-color, 40%);
$secondary-color: hsl(187, 77, 34);
//
$support-button: inherit;
$support-button-heart: #e83e8c;
@ -86,9 +84,12 @@ $variables: (
--mainColor: var(--mainColor),
--mainColorLighter: var(--mainColorLighter),
--mainColorLightest: var(--mainColorLightest),
--mainHoverColor: var(--mainHoverColor),
--mainBackgroundColor: var(--mainBackgroundColor),
--mainForegroundColor: var(--mainForegroundColor),
--secondaryColor: var(--secondaryColor),
--greyForegroundColor: var(--greyForegroundColor),

View File

@ -256,7 +256,7 @@ class Emailer {
}
myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) {
const importUrl = WEBSERVER.URL + '/my-account/video-imports'
const importUrl = WEBSERVER.URL + '/my-library/video-imports'
const text =
`Your video import "${videoImport.getTargetIdentifier()}" encountered an error.` +