From 4bb6886d28cc5333bbe1523674bf5db141af456f Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 23 Apr 2018 16:16:05 +0200 Subject: Rename account module to my-account --- client/src/app/my-account/index.ts | 3 + .../app/my-account/my-account-routing.module.ts | 41 +++++++ .../my-account-change-password/index.ts | 1 + .../my-account-change-password.component.html | 20 ++++ .../my-account-change-password.component.scss | 19 +++ .../my-account-change-password.component.ts | 62 ++++++++++ .../my-account-details/index.ts | 1 + .../my-account-details.component.html | 25 ++++ .../my-account-details.component.scss | 20 ++++ .../my-account-details.component.ts | 60 ++++++++++ .../my-account-settings.component.html | 24 ++++ .../my-account-settings.component.scss | 56 +++++++++ .../my-account-settings.component.ts | 74 ++++++++++++ .../my-account-videos.component.html | 45 +++++++ .../my-account-videos.component.scss | 114 ++++++++++++++++++ .../my-account-videos.component.ts | 133 +++++++++++++++++++++ .../src/app/my-account/my-account.component.html | 11 ++ client/src/app/my-account/my-account.component.ts | 7 ++ client/src/app/my-account/my-account.module.ts | 30 +++++ 19 files changed, 746 insertions(+) create mode 100644 client/src/app/my-account/index.ts create mode 100644 client/src/app/my-account/my-account-routing.module.ts create mode 100644 client/src/app/my-account/my-account-settings/my-account-change-password/index.ts create mode 100644 client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html create mode 100644 client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.scss create mode 100644 client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts create mode 100644 client/src/app/my-account/my-account-settings/my-account-details/index.ts create mode 100644 client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.html create mode 100644 client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.scss create mode 100644 client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.ts create mode 100644 client/src/app/my-account/my-account-settings/my-account-settings.component.html create mode 100644 client/src/app/my-account/my-account-settings/my-account-settings.component.scss create mode 100644 client/src/app/my-account/my-account-settings/my-account-settings.component.ts create mode 100644 client/src/app/my-account/my-account-videos/my-account-videos.component.html create mode 100644 client/src/app/my-account/my-account-videos/my-account-videos.component.scss create mode 100644 client/src/app/my-account/my-account-videos/my-account-videos.component.ts create mode 100644 client/src/app/my-account/my-account.component.html create mode 100644 client/src/app/my-account/my-account.component.ts create mode 100644 client/src/app/my-account/my-account.module.ts (limited to 'client/src/app/my-account') diff --git a/client/src/app/my-account/index.ts b/client/src/app/my-account/index.ts new file mode 100644 index 000000000..3df96dd7a --- /dev/null +++ b/client/src/app/my-account/index.ts @@ -0,0 +1,3 @@ +export * from './my-account-routing.module' +export * from './my-account.component' +export * from './my-account.module' diff --git a/client/src/app/my-account/my-account-routing.module.ts b/client/src/app/my-account/my-account-routing.module.ts new file mode 100644 index 000000000..5a61db41f --- /dev/null +++ b/client/src/app/my-account/my-account-routing.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from '@angular/core' +import { RouterModule, Routes } from '@angular/router' +import { MetaGuard } from '@ngx-meta/core' +import { LoginGuard } from '../core' +import { MyAccountComponent } from './my-account.component' +import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' +import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component' + +const myAccountRoutes: Routes = [ + { + path: 'my-account', + component: MyAccountComponent, + canActivateChild: [ MetaGuard, LoginGuard ], + children: [ + { + path: 'settings', + component: MyAccountSettingsComponent, + data: { + meta: { + title: 'Account settings' + } + } + }, + { + path: 'videos', + component: MyAccountVideosComponent, + data: { + meta: { + title: 'Account videos' + } + } + } + ] + } +] + +@NgModule({ + imports: [ RouterModule.forChild(myAccountRoutes) ], + exports: [ RouterModule ] +}) +export class MyAccountRoutingModule {} diff --git a/client/src/app/my-account/my-account-settings/my-account-change-password/index.ts b/client/src/app/my-account/my-account-settings/my-account-change-password/index.ts new file mode 100644 index 000000000..644047c5f --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-change-password/index.ts @@ -0,0 +1 @@ +export * from './my-account-change-password.component' diff --git a/client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html b/client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html new file mode 100644 index 000000000..b0e3cada4 --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html @@ -0,0 +1,20 @@ +
{{ error }}
+ +
+ + + +
+ {{ formErrors['new-password'] }} +
+ + + + +
diff --git a/client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.scss b/client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.scss new file mode 100644 index 000000000..f8279ffd3 --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.scss @@ -0,0 +1,19 @@ +@import '_variables'; +@import '_mixins'; + +input[type=password] { + @include peertube-input-text(340px); + display: block; + + &#new-confirmed-password { + margin-top: 15px; + } +} + +input[type=submit] { + @include peertube-button; + @include orange-button; + + margin-top: 15px; +} + diff --git a/client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts b/client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts new file mode 100644 index 000000000..80af668f9 --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts @@ -0,0 +1,62 @@ +import { Component, OnInit } from '@angular/core' +import { FormBuilder, FormGroup } from '@angular/forms' +import { NotificationsService } from 'angular2-notifications' +import { FormReactive, USER_PASSWORD, UserService } from '../../../shared' + +@Component({ + selector: 'my-account-change-password', + templateUrl: './my-account-change-password.component.html', + styleUrls: [ './my-account-change-password.component.scss' ] +}) +export class MyAccountChangePasswordComponent extends FormReactive implements OnInit { + error: string = null + + form: FormGroup + formErrors = { + 'new-password': '', + 'new-confirmed-password': '' + } + validationMessages = { + 'new-password': USER_PASSWORD.MESSAGES, + 'new-confirmed-password': USER_PASSWORD.MESSAGES + } + + constructor ( + private formBuilder: FormBuilder, + private notificationsService: NotificationsService, + private userService: UserService + ) { + super() + } + + buildForm () { + this.form = this.formBuilder.group({ + 'new-password': [ '', USER_PASSWORD.VALIDATORS ], + 'new-confirmed-password': [ '', USER_PASSWORD.VALIDATORS ] + }) + + this.form.valueChanges.subscribe(data => this.onValueChanged(data)) + } + + ngOnInit () { + this.buildForm() + } + + changePassword () { + const newPassword = this.form.value['new-password'] + const newConfirmedPassword = this.form.value['new-confirmed-password'] + + this.error = null + + if (newPassword !== newConfirmedPassword) { + this.error = 'The new password and the confirmed password do not correspond.' + return + } + + this.userService.changePassword(newPassword).subscribe( + () => this.notificationsService.success('Success', 'Password updated.'), + + err => this.error = err.message + ) + } +} diff --git a/client/src/app/my-account/my-account-settings/my-account-details/index.ts b/client/src/app/my-account/my-account-settings/my-account-details/index.ts new file mode 100644 index 000000000..b7f58e329 --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-details/index.ts @@ -0,0 +1 @@ +export * from './my-account-details.component' diff --git a/client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.html b/client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.html new file mode 100644 index 000000000..0e8598e9e --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.html @@ -0,0 +1,25 @@ +
+
+ + + +
+ +
+
+ +
+ + + +
+ + +
diff --git a/client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.scss b/client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.scss new file mode 100644 index 000000000..ed59e4689 --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.scss @@ -0,0 +1,20 @@ +@import '_variables'; +@import '_mixins'; + +input[type=checkbox] { + @include peertube-checkbox(1px); +} + +input[type=submit] { + @include peertube-button; + @include orange-button; + + display: block; + margin-top: 15px; +} + +.peertube-select-container { + @include peertube-select-container(340px); + + margin-bottom: 30px; +} \ No newline at end of file diff --git a/client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.ts b/client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.ts new file mode 100644 index 000000000..4c1456541 --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-details/my-account-details.component.ts @@ -0,0 +1,60 @@ +import { Component, Input, OnInit } from '@angular/core' +import { FormBuilder, FormGroup } from '@angular/forms' +import { NotificationsService } from 'angular2-notifications' +import { UserUpdateMe } from '../../../../../../shared' +import { AuthService } from '../../../core' +import { FormReactive, User, UserService } from '../../../shared' + +@Component({ + selector: 'my-account-details', + templateUrl: './my-account-details.component.html', + styleUrls: [ './my-account-details.component.scss' ] +}) +export class MyAccountDetailsComponent extends FormReactive implements OnInit { + @Input() user: User = null + + form: FormGroup + formErrors = {} + validationMessages = {} + + constructor ( + private authService: AuthService, + private formBuilder: FormBuilder, + private notificationsService: NotificationsService, + private userService: UserService + ) { + super() + } + + buildForm () { + this.form = this.formBuilder.group({ + nsfwPolicy: [ this.user.nsfwPolicy ], + autoPlayVideo: [ this.user.autoPlayVideo ] + }) + + this.form.valueChanges.subscribe(data => this.onValueChanged(data)) + } + + ngOnInit () { + this.buildForm() + } + + updateDetails () { + const nsfwPolicy = this.form.value['nsfwPolicy'] + const autoPlayVideo = this.form.value['autoPlayVideo'] + const details: UserUpdateMe = { + nsfwPolicy, + autoPlayVideo + } + + this.userService.updateMyDetails(details).subscribe( + () => { + this.notificationsService.success('Success', 'Information updated.') + + this.authService.refreshUserInformation() + }, + + err => this.notificationsService.error('Error', err.message) + ) + } +} diff --git a/client/src/app/my-account/my-account-settings/my-account-settings.component.html b/client/src/app/my-account/my-account-settings/my-account-settings.component.html new file mode 100644 index 000000000..7ae27dc75 --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-settings.component.html @@ -0,0 +1,24 @@ +
+ Avatar + + +
+ +
+ Change your avatar + +
+
(extensions: {{ avatarExtensions }}, max size: {{ maxAvatarSize | bytes }})
+ +
+ Video quota: {{ userVideoQuotaUsed | bytes: 0 }} / {{ userVideoQuota }} +
+ +
Account settings
+ + +
Video settings
+ diff --git a/client/src/app/my-account/my-account-settings/my-account-settings.component.scss b/client/src/app/my-account/my-account-settings/my-account-settings.component.scss new file mode 100644 index 000000000..1cc00ca49 --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-settings.component.scss @@ -0,0 +1,56 @@ +@import '_variables'; +@import '_mixins'; + +.user { + display: flex; + + img { + @include avatar(50px); + + margin-right: 15px; + } + + .user-info { + .user-info-username { + font-size: 20px; + font-weight: $font-bold; + } + + .user-info-followers { + font-size: 15px; + } + } +} + +.button-file { + @include peertube-button-file(160px); + + margin-top: 10px; + margin-bottom: 5px; +} + +.file-max-size { + display: inline-block; + font-size: 13px; + + position: relative; + top: -10px; +} + +.user-quota { + font-size: 15px; + margin-top: 20px; + + .user-quota-label { + font-weight: $font-semibold; + } +} + +.account-title { + text-transform: uppercase; + color: $orange-color; + font-weight: $font-bold; + font-size: 13px; + margin-top: 55px; + margin-bottom: 30px; +} diff --git a/client/src/app/my-account/my-account-settings/my-account-settings.component.ts b/client/src/app/my-account/my-account-settings/my-account-settings.component.ts new file mode 100644 index 000000000..91420cc6f --- /dev/null +++ b/client/src/app/my-account/my-account-settings/my-account-settings.component.ts @@ -0,0 +1,74 @@ +import { Component, OnInit, ViewChild } from '@angular/core' +import { NotificationsService } from 'angular2-notifications' +import { BytesPipe } from 'ngx-pipes' +import { AuthService } from '../../core' +import { ServerService } from '../../core/server' +import { User } from '../../shared' +import { UserService } from '../../shared/users' + +@Component({ + selector: 'my-account-settings', + templateUrl: './my-account-settings.component.html', + styleUrls: [ './my-account-settings.component.scss' ] +}) +export class MyAccountSettingsComponent implements OnInit { + @ViewChild('avatarfileInput') avatarfileInput + + user: User = null + userVideoQuota = '0' + userVideoQuotaUsed = 0 + + constructor ( + private userService: UserService, + private authService: AuthService, + private serverService: ServerService, + private notificationsService: NotificationsService + ) {} + + ngOnInit () { + this.user = this.authService.getUser() + + this.authService.userInformationLoaded.subscribe( + () => { + if (this.user.videoQuota !== -1) { + this.userVideoQuota = new BytesPipe().transform(this.user.videoQuota, 0).toString() + } else { + this.userVideoQuota = 'Unlimited' + } + } + ) + + this.userService.getMyVideoQuotaUsed() + .subscribe(data => this.userVideoQuotaUsed = data.videoQuotaUsed) + } + + getAvatarUrl () { + return this.user.getAvatarUrl() + } + + changeAvatar () { + const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ] + + const formData = new FormData() + formData.append('avatarfile', avatarfile) + + this.userService.changeAvatar(formData) + .subscribe( + data => { + this.notificationsService.success('Success', 'Avatar changed.') + + this.user.account.avatar = data.avatar + }, + + err => this.notificationsService.error('Error', err.message) + ) + } + + get maxAvatarSize () { + return this.serverService.getConfig().avatar.file.size.max + } + + get avatarExtensions () { + return this.serverService.getConfig().avatar.file.extensions.join(',') + } +} diff --git a/client/src/app/my-account/my-account-videos/my-account-videos.component.html b/client/src/app/my-account/my-account-videos/my-account-videos.component.html new file mode 100644 index 000000000..66ce3a77b --- /dev/null +++ b/client/src/app/my-account/my-account-videos/my-account-videos.component.html @@ -0,0 +1,45 @@ +
No results.
+ +
+
+
+
+ + +
+ + + +
+ {{ video.name }} + {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views +
{{ video.privacy.label }}
+
+ + +
+
+ + Cancel + + + + + Delete + +
+
+ +
+ + + +
+
+
+
diff --git a/client/src/app/my-account/my-account-videos/my-account-videos.component.scss b/client/src/app/my-account/my-account-videos/my-account-videos.component.scss new file mode 100644 index 000000000..f276ea389 --- /dev/null +++ b/client/src/app/my-account/my-account-videos/my-account-videos.component.scss @@ -0,0 +1,114 @@ +@import '_variables'; +@import '_mixins'; + +.action-selection-mode { + width: 174px; + display: flex; + justify-content: flex-end; + + .action-selection-mode-child { + position: fixed; + + .action-button { + display: inline-block; + } + + .action-button-cancel-selection { + @include peertube-button; + @include grey-button; + + margin-right: 10px; + } + + .action-button-delete-selection { + @include peertube-button; + @include orange-button; + } + + .icon.icon-delete-white { + @include icon(21px); + + position: relative; + top: -2px; + background-image: url('../../../assets/images/global/delete-white.svg'); + } + } +} + +/deep/ .action-button { + &.action-button-delete { + margin-right: 10px; + } +} + +.video { + display: flex; + min-height: 130px; + padding-bottom: 20px; + margin-bottom: 20px; + border-bottom: 1px solid #C6C6C6; + + &:first-child { + margin-top: 47px; + } + + .checkbox-container { + display: flex; + align-items: center; + margin-right: 20px; + margin-left: 12px; + + input[type=checkbox] { + @include peertube-checkbox(2px); + } + } + + my-video-thumbnail { + margin-right: 10px; + } + + .video-info { + flex-grow: 1; + + .video-info-name { + @include disable-default-a-behaviour; + + color: #000; + display: block; + font-size: 16px; + font-weight: $font-semibold; + } + + .video-info-date-views, .video-info-private { + font-size: 13px; + + &.video-info-private { + font-weight: $font-semibold; + } + } + } + + .video-buttons { + min-width: 190px; + } +} + +@media screen and (max-width: 800px) { + .video { + flex-direction: column; + height: auto; + text-align: center; + + input[type=checkbox] { + display: none; + } + + my-video-thumbnail { + margin-right: 0; + } + + .video-buttons { + margin-top: 10px; + } + } +} diff --git a/client/src/app/my-account/my-account-videos/my-account-videos.component.ts b/client/src/app/my-account/my-account-videos/my-account-videos.component.ts new file mode 100644 index 000000000..a6cef361e --- /dev/null +++ b/client/src/app/my-account/my-account-videos/my-account-videos.component.ts @@ -0,0 +1,133 @@ +import { Component, OnInit, OnDestroy } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { Location } from '@angular/common' +import { immutableAssign } from '@app/shared/misc/utils' +import { ComponentPagination } from '@app/shared/rest/component-pagination.model' +import { NotificationsService } from 'angular2-notifications' +import 'rxjs/add/observable/from' +import 'rxjs/add/operator/concatAll' +import { Observable } from 'rxjs/Observable' +import { AuthService } from '../../core/auth' +import { ConfirmService } from '../../core/confirm' +import { AbstractVideoList } from '../../shared/video/abstract-video-list' +import { Video } from '../../shared/video/video.model' +import { VideoService } from '../../shared/video/video.service' + +@Component({ + selector: 'my-account-videos', + templateUrl: './my-account-videos.component.html', + styleUrls: [ './my-account-videos.component.scss' ] +}) +export class MyAccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { + titlePage = 'My videos' + currentRoute = '/my-account/videos' + checkedVideos: { [ id: number ]: boolean } = {} + pagination: ComponentPagination = { + currentPage: 1, + itemsPerPage: 5, + totalItems: null + } + + protected baseVideoWidth = -1 + protected baseVideoHeight = 155 + + constructor (protected router: Router, + protected route: ActivatedRoute, + protected authService: AuthService, + protected notificationsService: NotificationsService, + protected confirmService: ConfirmService, + protected location: Location, + private videoService: VideoService) { + super() + } + + ngOnInit () { + super.ngOnInit() + + // this.generateSyndicationList() + } + + ngOnDestroy () { + super.ngOnDestroy() + } + + abortSelectionMode () { + this.checkedVideos = {} + } + + isInSelectionMode () { + return Object.keys(this.checkedVideos).some(k => this.checkedVideos[k] === true) + } + + getVideosObservable (page: number) { + const newPagination = immutableAssign(this.pagination, { currentPage: page }) + + return this.videoService.getMyVideos(newPagination, this.sort) + } + + generateSyndicationList () { + throw new Error('Method not implemented.') + } + + async deleteSelectedVideos () { + const toDeleteVideosIds = Object.keys(this.checkedVideos) + .filter(k => this.checkedVideos[k] === true) + .map(k => parseInt(k, 10)) + + const res = await this.confirmService.confirm(`Do you really want to delete ${toDeleteVideosIds.length} videos?`, 'Delete') + if (res === false) return + + const observables: Observable[] = [] + for (const videoId of toDeleteVideosIds) { + const o = this.videoService + .removeVideo(videoId) + .do(() => this.spliceVideosById(videoId)) + + observables.push(o) + } + + Observable.from(observables) + .concatAll() + .subscribe( + res => { + this.notificationsService.success('Success', `${toDeleteVideosIds.length} videos deleted.`) + this.buildVideoPages() + }, + + err => this.notificationsService.error('Error', err.message) + ) + } + + async deleteVideo (video: Video) { + const res = await this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete') + if (res === false) return + + this.videoService.removeVideo(video.id) + .subscribe( + status => { + this.notificationsService.success('Success', `Video ${video.name} deleted.`) + this.spliceVideosById(video.id) + this.buildVideoPages() + }, + + error => this.notificationsService.error('Error', error.message) + ) + } + + protected buildVideoHeight () { + // In account videos, the video height is fixed + return this.baseVideoHeight + } + + private spliceVideosById (id: number) { + for (const key of Object.keys(this.loadedPages)) { + const videos = this.loadedPages[key] + const index = videos.findIndex(v => v.id === id) + + if (index !== -1) { + videos.splice(index, 1) + return + } + } + } +} diff --git a/client/src/app/my-account/my-account.component.html b/client/src/app/my-account/my-account.component.html new file mode 100644 index 000000000..637b2587c --- /dev/null +++ b/client/src/app/my-account/my-account.component.html @@ -0,0 +1,11 @@ +
+ + +
+ +
+
diff --git a/client/src/app/my-account/my-account.component.ts b/client/src/app/my-account/my-account.component.ts new file mode 100644 index 000000000..0955e2b7b --- /dev/null +++ b/client/src/app/my-account/my-account.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core' + +@Component({ + selector: 'my-account', + templateUrl: './my-account.component.html' +}) +export class MyAccountComponent {} diff --git a/client/src/app/my-account/my-account.module.ts b/client/src/app/my-account/my-account.module.ts new file mode 100644 index 000000000..317277304 --- /dev/null +++ b/client/src/app/my-account/my-account.module.ts @@ -0,0 +1,30 @@ +import { NgModule } from '@angular/core' +import { SharedModule } from '../shared' +import { MyAccountRoutingModule } from './my-account-routing.module' +import { MyAccountChangePasswordComponent } from './my-account-settings/my-account-change-password/my-account-change-password.component' +import { MyAccountDetailsComponent } from './my-account-settings/my-account-details/my-account-details.component' +import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' +import { MyAccountComponent } from './my-account.component' +import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component' + +@NgModule({ + imports: [ + MyAccountRoutingModule, + SharedModule + ], + + declarations: [ + MyAccountComponent, + MyAccountSettingsComponent, + MyAccountChangePasswordComponent, + MyAccountDetailsComponent, + MyAccountVideosComponent + ], + + exports: [ + MyAccountComponent + ], + + providers: [] +}) +export class MyAccountModule { } -- cgit v1.2.3