mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2024-09-11 21:47:09 +03:00
Add video channel management
This commit is contained in:
parent
7de6afdf54
commit
08c1efbe32
@ -1,4 +1,4 @@
|
||||
<div class="admin-sub-title">Update PeerTube configuration</div>
|
||||
<div class="form-sub-title">Update PeerTube configuration</div>
|
||||
|
||||
<form role="form" [formGroup]="form">
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="admin-sub-header">
|
||||
<div class="admin-sub-title">Manage follows</div>
|
||||
<div class="form-sub-title">Manage follows</div>
|
||||
|
||||
<tabset #followsMenuTabs>
|
||||
<tab *ngFor="let link of links">
|
||||
|
@ -1,4 +1,4 @@
|
||||
.admin-sub-title {
|
||||
.form-sub-title {
|
||||
flex-grow: 0;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="admin-sub-header">
|
||||
<div class="admin-sub-title">Jobs list</div>
|
||||
<div class="form-sub-title">Jobs list</div>
|
||||
|
||||
<div class="peertube-select-container">
|
||||
<select [(ngModel)]="jobState" (ngModelChange)="onJobStateChanged()">
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="admin-sub-title" *ngIf="isCreation() === true">Add user</div>
|
||||
<div class="admin-sub-title" *ngIf="isCreation() === false">Edit user {{ username }}</div>
|
||||
<div class="form-sub-title" *ngIf="isCreation() === true">Add user</div>
|
||||
<div class="form-sub-title" *ngIf="isCreation() === false">Edit user {{ username }}</div>
|
||||
|
||||
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
.admin-sub-title {
|
||||
.form-sub-title {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="admin-sub-header">
|
||||
<div class="admin-sub-title">Users list</div>
|
||||
<div class="form-sub-title">Users list</div>
|
||||
|
||||
<a class="add-button" routerLink="/admin/users/add">
|
||||
<span class="icon icon-add"></span>
|
||||
|
@ -2,13 +2,5 @@
|
||||
@import '_mixins';
|
||||
|
||||
.add-button {
|
||||
@include peertube-button-link;
|
||||
@include orange-button;
|
||||
|
||||
.icon.icon-add {
|
||||
@include icon(22px);
|
||||
|
||||
margin-right: 3px;
|
||||
background-image: url('../../../../assets/images/admin/add.svg');
|
||||
}
|
||||
@include create-button;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="admin-sub-header">
|
||||
<div class="admin-sub-title">Video abuses list</div>
|
||||
<div class="form-sub-title">Video abuses list</div>
|
||||
</div>
|
||||
|
||||
<p-table
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="admin-sub-header">
|
||||
<div class="admin-sub-title">Blacklisted videos</div>
|
||||
<div class="form-sub-title">Blacklisted videos</div>
|
||||
</div>
|
||||
|
||||
<p-table
|
||||
|
@ -5,6 +5,9 @@ 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'
|
||||
import { MyAccountVideoChannelsComponent } from '@app/my-account/my-account-video-channels/my-account-video-channels.component'
|
||||
import { MyAccountVideoChannelCreateComponent } from '@app/my-account/my-account-video-channels/my-account-video-channel-create.component'
|
||||
import { MyAccountVideoChannelUpdateComponent } from '@app/my-account/my-account-video-channels/my-account-video-channel-update.component'
|
||||
|
||||
const myAccountRoutes: Routes = [
|
||||
{
|
||||
@ -21,6 +24,33 @@ const myAccountRoutes: Routes = [
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'video-channels',
|
||||
component: MyAccountVideoChannelsComponent,
|
||||
data: {
|
||||
meta: {
|
||||
title: 'Account video channels'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'video-channels/create',
|
||||
component: MyAccountVideoChannelCreateComponent,
|
||||
data: {
|
||||
meta: {
|
||||
title: 'Create new video channel'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'video-channels/update/:videoChannelId',
|
||||
component: MyAccountVideoChannelUpdateComponent,
|
||||
data: {
|
||||
meta: {
|
||||
title: 'Update video channel'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'videos',
|
||||
component: MyAccountVideosComponent,
|
||||
|
@ -0,0 +1,86 @@
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import 'rxjs/add/observable/from'
|
||||
import 'rxjs/add/operator/concatAll'
|
||||
import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
|
||||
import { FormBuilder, FormGroup } from '@angular/forms'
|
||||
import { VideoChannelCreate } from '../../../../../shared/models/videos'
|
||||
import {
|
||||
VIDEO_CHANNEL_DESCRIPTION,
|
||||
VIDEO_CHANNEL_DISPLAY_NAME,
|
||||
VIDEO_CHANNEL_SUPPORT
|
||||
} from '@app/shared/forms/form-validators/video-channel'
|
||||
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-video-channel-create',
|
||||
templateUrl: './my-account-video-channel-edit.component.html',
|
||||
styleUrls: [ './my-account-video-channel-edit.component.scss' ]
|
||||
})
|
||||
export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelEdit implements OnInit {
|
||||
error: string
|
||||
|
||||
form: FormGroup
|
||||
formErrors = {
|
||||
'display-name': '',
|
||||
'description': '',
|
||||
'support': ''
|
||||
}
|
||||
validationMessages = {
|
||||
'display-name': VIDEO_CHANNEL_DISPLAY_NAME.MESSAGES,
|
||||
'description': VIDEO_CHANNEL_DESCRIPTION.MESSAGES,
|
||||
'support': VIDEO_CHANNEL_SUPPORT.MESSAGES
|
||||
}
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private router: Router,
|
||||
private formBuilder: FormBuilder,
|
||||
private videoChannelService: VideoChannelService
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
buildForm () {
|
||||
this.form = this.formBuilder.group({
|
||||
'display-name': [ '', VIDEO_CHANNEL_DISPLAY_NAME.VALIDATORS ],
|
||||
description: [ '', VIDEO_CHANNEL_DESCRIPTION.VALIDATORS ],
|
||||
support: [ '', VIDEO_CHANNEL_SUPPORT.VALIDATORS ]
|
||||
})
|
||||
|
||||
this.form.valueChanges.subscribe(data => this.onValueChanged(data))
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.buildForm()
|
||||
}
|
||||
|
||||
formValidated () {
|
||||
this.error = undefined
|
||||
|
||||
const body = this.form.value
|
||||
const videoChannelCreate: VideoChannelCreate = {
|
||||
displayName: body['display-name'],
|
||||
description: body.description,
|
||||
support: body.support
|
||||
}
|
||||
|
||||
this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success('Success', `Video channel ${videoChannelCreate.displayName} created.`)
|
||||
this.router.navigate([ '/my-account', 'video-channels' ])
|
||||
},
|
||||
|
||||
err => this.error = err.message
|
||||
)
|
||||
}
|
||||
|
||||
isCreation () {
|
||||
return true
|
||||
}
|
||||
|
||||
getFormButtonTitle () {
|
||||
return 'Create this video channel'
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<div class="form-sub-title" *ngIf="isCreation() === true">Create a video channel</div>
|
||||
<div class="form-sub-title" *ngIf="isCreation() === false">Update {{ videoChannel?.displayName }}</div>
|
||||
|
||||
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||
|
||||
<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
|
||||
<div class="form-group">
|
||||
<label for="display-name">Display name</label>
|
||||
<input
|
||||
type="text" id="display-name"
|
||||
formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.display-name" class="form-error">
|
||||
{{ formErrors.display-name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea
|
||||
id="description" formControlName="description"
|
||||
[ngClass]="{ 'input-error': formErrors['description'] }"
|
||||
></textarea>
|
||||
<div *ngIf="formErrors.description" class="form-error">
|
||||
{{ formErrors.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="support">Support</label>
|
||||
<my-help helpType="markdownEnhanced" preHtml="Short text to tell people how they can support your channel (membership platform...)."></my-help>
|
||||
<my-markdown-textarea
|
||||
id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced"
|
||||
[classes]="{ 'input-error': formErrors['support'] }"
|
||||
></my-markdown-textarea>
|
||||
<div *ngIf="formErrors.support" class="form-error">
|
||||
{{ formErrors.support }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid">
|
||||
</form>
|
@ -0,0 +1,27 @@
|
||||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
.form-sub-title {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
@include peertube-input-text(340px);
|
||||
|
||||
display: block;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@include peertube-textarea(500px, 150px);
|
||||
|
||||
display: block;
|
||||
}
|
||||
|
||||
.peertube-select-container {
|
||||
@include peertube-select-container(340px);
|
||||
}
|
||||
|
||||
input[type=submit] {
|
||||
@include peertube-button;
|
||||
@include orange-button;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { FormReactive } from '@app/shared'
|
||||
|
||||
export abstract class MyAccountVideoChannelEdit extends FormReactive {
|
||||
abstract isCreation (): boolean
|
||||
abstract getFormButtonTitle (): string
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import 'rxjs/add/observable/from'
|
||||
import 'rxjs/add/operator/concatAll'
|
||||
import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
|
||||
import { FormBuilder, FormGroup } from '@angular/forms'
|
||||
import { VideoChannelUpdate } from '../../../../../shared/models/videos'
|
||||
import {
|
||||
VIDEO_CHANNEL_DESCRIPTION,
|
||||
VIDEO_CHANNEL_DISPLAY_NAME,
|
||||
VIDEO_CHANNEL_SUPPORT
|
||||
} from '@app/shared/forms/form-validators/video-channel'
|
||||
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
|
||||
import { Subscription } from 'rxjs/Subscription'
|
||||
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-video-channel-update',
|
||||
templateUrl: './my-account-video-channel-edit.component.html',
|
||||
styleUrls: [ './my-account-video-channel-edit.component.scss' ]
|
||||
})
|
||||
export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelEdit implements OnInit, OnDestroy {
|
||||
error: string
|
||||
|
||||
form: FormGroup
|
||||
formErrors = {
|
||||
'display-name': '',
|
||||
'description': '',
|
||||
'support': ''
|
||||
}
|
||||
validationMessages = {
|
||||
'display-name': VIDEO_CHANNEL_DISPLAY_NAME.MESSAGES,
|
||||
'description': VIDEO_CHANNEL_DESCRIPTION.MESSAGES,
|
||||
'support': VIDEO_CHANNEL_SUPPORT.MESSAGES
|
||||
}
|
||||
|
||||
private videoChannelToUpdate: VideoChannel
|
||||
private paramsSub: Subscription
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private formBuilder: FormBuilder,
|
||||
private videoChannelService: VideoChannelService
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
buildForm () {
|
||||
this.form = this.formBuilder.group({
|
||||
'display-name': [ '', VIDEO_CHANNEL_DISPLAY_NAME.VALIDATORS ],
|
||||
description: [ '', VIDEO_CHANNEL_DESCRIPTION.VALIDATORS ],
|
||||
support: [ '', VIDEO_CHANNEL_SUPPORT.VALIDATORS ]
|
||||
})
|
||||
|
||||
this.form.valueChanges.subscribe(data => this.onValueChanged(data))
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.buildForm()
|
||||
|
||||
this.paramsSub = this.route.params.subscribe(routeParams => {
|
||||
const videoChannelId = routeParams['videoChannelId']
|
||||
|
||||
this.videoChannelService.getVideoChannel(videoChannelId).subscribe(
|
||||
videoChannelToUpdate => {
|
||||
this.videoChannelToUpdate = videoChannelToUpdate
|
||||
|
||||
this.form.patchValue({
|
||||
'display-name': videoChannelToUpdate.displayName,
|
||||
description: videoChannelToUpdate.description,
|
||||
support: videoChannelToUpdate.support
|
||||
})
|
||||
},
|
||||
|
||||
err => this.error = err.message
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
if (this.paramsSub) this.paramsSub.unsubscribe()
|
||||
}
|
||||
|
||||
formValidated () {
|
||||
this.error = undefined
|
||||
|
||||
const body = this.form.value
|
||||
const videoChannelUpdate: VideoChannelUpdate = {
|
||||
displayName: body['display-name'],
|
||||
description: body.description,
|
||||
support: body.support
|
||||
}
|
||||
|
||||
this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.uuid, videoChannelUpdate).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success('Success', `Video channel ${videoChannelUpdate.displayName} updated.`)
|
||||
this.router.navigate([ '/my-account', 'video-channels' ])
|
||||
},
|
||||
|
||||
err => this.error = err.message
|
||||
)
|
||||
}
|
||||
|
||||
isCreation () {
|
||||
return false
|
||||
}
|
||||
|
||||
getFormButtonTitle () {
|
||||
return this.videoChannelToUpdate
|
||||
? 'Update ' + this.videoChannelToUpdate.displayName + ' video channel'
|
||||
: 'Update'
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<div class="video-channels-header">
|
||||
<a class="create-button" routerLink="create">
|
||||
<span class="icon icon-add"></span>
|
||||
Create another video channel
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="video-channels">
|
||||
<div *ngFor="let videoChannel of videoChannels" class="video-channel">
|
||||
<a [routerLink]="[ '/video-channels', videoChannel.uuid ]">
|
||||
<img [src]="videoChannel.avatarUrl" alt="Avatar" />
|
||||
</a>
|
||||
|
||||
<div class="video-channel-info">
|
||||
<a [routerLink]="[ '/video-channels', videoChannel.uuid ]" class="video-channel-names" title="Go to the channel">
|
||||
<div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
|
||||
<!-- Hide the name for now, because it's an UUID not very friendly -->
|
||||
<!--<div class="video-channel-name">{{ videoChannel.name }}</div>-->
|
||||
</a>
|
||||
|
||||
<div class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
|
||||
</div>
|
||||
|
||||
<div class="video-channel-buttons">
|
||||
<my-delete-button (click)="deleteVideoChannel(videoChannel)"></my-delete-button>
|
||||
|
||||
<my-edit-button [routerLink]="[ 'update', videoChannel.uuid ]"></my-edit-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,72 @@
|
||||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
.create-button {
|
||||
@include create-button;
|
||||
}
|
||||
|
||||
/deep/ .action-button {
|
||||
&.action-button-delete {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.video-channel {
|
||||
display: flex;
|
||||
min-height: 130px;
|
||||
padding-bottom: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid #C6C6C6;
|
||||
|
||||
img {
|
||||
@include avatar(80px);
|
||||
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.video-channel-info {
|
||||
flex-grow: 1;
|
||||
|
||||
a.video-channel-names {
|
||||
@include disable-default-a-behaviour;
|
||||
|
||||
display: flex;
|
||||
color: #000;
|
||||
|
||||
.video-channel-display-name {
|
||||
font-weight: $font-semibold;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.video-channel-name {
|
||||
font-size: 14px;
|
||||
color: #777272;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.video-channel-buttons {
|
||||
min-width: 190px;
|
||||
}
|
||||
}
|
||||
|
||||
.video-channels-header {
|
||||
text-align: right;
|
||||
margin: 20px 0 50px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.video-channel {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.video-channel-buttons {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import 'rxjs/add/observable/from'
|
||||
import 'rxjs/add/operator/concatAll'
|
||||
import { AuthService } from '../../core/auth'
|
||||
import { ConfirmService } from '../../core/confirm'
|
||||
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
|
||||
import { User } from '@app/shared'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-video-channels',
|
||||
templateUrl: './my-account-video-channels.component.html',
|
||||
styleUrls: [ './my-account-video-channels.component.scss' ]
|
||||
})
|
||||
export class MyAccountVideoChannelsComponent implements OnInit{
|
||||
videoChannels: VideoChannel[] = []
|
||||
|
||||
private user: User
|
||||
|
||||
constructor (
|
||||
private authService: AuthService,
|
||||
private notificationsService: NotificationsService,
|
||||
private confirmService: ConfirmService,
|
||||
private videoChannelService: VideoChannelService
|
||||
) {}
|
||||
|
||||
ngOnInit () {
|
||||
this.user = this.authService.getUser()
|
||||
|
||||
this.loadVideoChannels()
|
||||
}
|
||||
|
||||
async deleteVideoChannel (videoChannel: VideoChannel) {
|
||||
const res = await this.confirmService.confirmWithInput(
|
||||
`Do you really want to delete ${videoChannel.displayName}? It will delete all videos uploaded in this channel too.`,
|
||||
'Please type the name of the video channel to confirm',
|
||||
videoChannel.displayName,
|
||||
'Delete'
|
||||
)
|
||||
if (res === false) return
|
||||
|
||||
this.videoChannelService.removeVideoChannel(videoChannel)
|
||||
.subscribe(
|
||||
status => {
|
||||
this.loadVideoChannels()
|
||||
this.notificationsService.success('Success', `Video channel ${videoChannel.name} deleted.`)
|
||||
},
|
||||
|
||||
error => this.notificationsService.error('Error', error.message)
|
||||
)
|
||||
}
|
||||
|
||||
private loadVideoChannels () {
|
||||
this.authService.userInformationLoaded
|
||||
.flatMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account.id))
|
||||
.subscribe(res => this.videoChannels = res.data)
|
||||
}
|
||||
}
|
@ -43,8 +43,6 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
||||
|
||||
ngOnInit () {
|
||||
super.ngOnInit()
|
||||
|
||||
// this.generateSyndicationList()
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
|
@ -2,6 +2,8 @@
|
||||
<div class="sub-menu">
|
||||
<a routerLink="/my-account/settings" routerLinkActive="active" class="title-page">My settings</a>
|
||||
|
||||
<a routerLink="/my-account/video-channels" routerLinkActive="active" class="title-page">My video channels</a>
|
||||
|
||||
<a routerLink="/my-account/videos" routerLinkActive="active" class="title-page">My videos</a>
|
||||
</div>
|
||||
|
||||
|
@ -7,6 +7,9 @@ import { MyAccountSettingsComponent } from './my-account-settings/my-account-set
|
||||
import { MyAccountComponent } from './my-account.component'
|
||||
import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component'
|
||||
import { MyAccountProfileComponent } from '@app/my-account/my-account-settings/my-account-profile/my-account-profile.component'
|
||||
import { MyAccountVideoChannelsComponent } from '@app/my-account/my-account-video-channels/my-account-video-channels.component'
|
||||
import { MyAccountVideoChannelCreateComponent } from '@app/my-account/my-account-video-channels/my-account-video-channel-create.component'
|
||||
import { MyAccountVideoChannelUpdateComponent } from '@app/my-account/my-account-video-channels/my-account-video-channel-update.component'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -20,7 +23,10 @@ import { MyAccountProfileComponent } from '@app/my-account/my-account-settings/m
|
||||
MyAccountChangePasswordComponent,
|
||||
MyAccountVideoSettingsComponent,
|
||||
MyAccountProfileComponent,
|
||||
MyAccountVideosComponent
|
||||
MyAccountVideosComponent,
|
||||
MyAccountVideoChannelsComponent,
|
||||
MyAccountVideoChannelCreateComponent,
|
||||
MyAccountVideoChannelUpdateComponent
|
||||
],
|
||||
|
||||
exports: [
|
||||
|
34
client/src/app/shared/forms/form-validators/video-channel.ts
Normal file
34
client/src/app/shared/forms/form-validators/video-channel.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Validators } from '@angular/forms'
|
||||
|
||||
export const VIDEO_CHANNEL_DISPLAY_NAME = {
|
||||
VALIDATORS: [
|
||||
Validators.required,
|
||||
Validators.minLength(3),
|
||||
Validators.maxLength(120)
|
||||
],
|
||||
MESSAGES: {
|
||||
'required': 'Display name is required.',
|
||||
'minlength': 'Display name must be at least 3 characters long.',
|
||||
'maxlength': 'Display name cannot be more than 120 characters long.'
|
||||
}
|
||||
}
|
||||
export const VIDEO_CHANNEL_DESCRIPTION = {
|
||||
VALIDATORS: [
|
||||
Validators.minLength(3),
|
||||
Validators.maxLength(250)
|
||||
],
|
||||
MESSAGES: {
|
||||
'minlength': 'Description must be at least 3 characters long.',
|
||||
'maxlength': 'Description cannot be more than 250 characters long.'
|
||||
}
|
||||
}
|
||||
export const VIDEO_CHANNEL_SUPPORT = {
|
||||
VALIDATORS: [
|
||||
Validators.minLength(3),
|
||||
Validators.maxLength(300)
|
||||
],
|
||||
MESSAGES: {
|
||||
'minlength': 'Support text must be at least 3 characters long.',
|
||||
'maxlength': 'Support text cannot be more than 300 characters long.'
|
||||
}
|
||||
}
|
@ -5,12 +5,14 @@ import { Observable } from 'rxjs/Observable'
|
||||
import { RestExtractor } from '../rest/rest-extractor.service'
|
||||
import { RestService } from '../rest/rest.service'
|
||||
import { HttpClient } from '@angular/common/http'
|
||||
import { VideoChannel as VideoChannelServer } from '../../../../../shared/models/videos'
|
||||
import { VideoChannel as VideoChannelServer, VideoChannelCreate, VideoChannelUpdate } from '../../../../../shared/models/videos'
|
||||
import { AccountService } from '../account/account.service'
|
||||
import { ResultList } from '../../../../../shared'
|
||||
import { VideoChannel } from './video-channel.model'
|
||||
import { ReplaySubject } from 'rxjs/ReplaySubject'
|
||||
import { environment } from '../../../environments/environment'
|
||||
import { UserService } from '@app/+admin/users/shared/user.service'
|
||||
import { User } from '@app/shared'
|
||||
|
||||
@Injectable()
|
||||
export class VideoChannelService {
|
||||
@ -37,6 +39,24 @@ export class VideoChannelService {
|
||||
.catch((res) => this.restExtractor.handleError(res))
|
||||
}
|
||||
|
||||
createVideoChannel (videoChannel: VideoChannelCreate) {
|
||||
return this.authHttp.post(VideoChannelService.BASE_VIDEO_CHANNEL_URL, videoChannel)
|
||||
.map(this.restExtractor.extractDataBool)
|
||||
.catch(err => this.restExtractor.handleError(err))
|
||||
}
|
||||
|
||||
updateVideoChannel (videoChannelUUID: string, videoChannel: VideoChannelUpdate) {
|
||||
return this.authHttp.put(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelUUID, videoChannel)
|
||||
.map(this.restExtractor.extractDataBool)
|
||||
.catch(err => this.restExtractor.handleError(err))
|
||||
}
|
||||
|
||||
removeVideoChannel (videoChannel: VideoChannel) {
|
||||
return this.authHttp.delete(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid)
|
||||
.map(this.restExtractor.extractDataBool)
|
||||
.catch(err => this.restExtractor.handleError(err))
|
||||
}
|
||||
|
||||
private extractVideoChannels (result: ResultList<VideoChannelServer>) {
|
||||
const videoChannels: VideoChannel[] = []
|
||||
|
||||
|
@ -118,12 +118,12 @@ label {
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
|
||||
.admin-sub-title {
|
||||
.form-sub-title {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-sub-title {
|
||||
.form-sub-title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -377,4 +377,16 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin create-button {
|
||||
@include peertube-button-link;
|
||||
@include orange-button;
|
||||
|
||||
.icon.icon-add {
|
||||
@include icon(22px);
|
||||
|
||||
margin-right: 3px;
|
||||
background-image: url('/assets/images/admin/add.svg');
|
||||
}
|
||||
}
|
@ -138,7 +138,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
|
||||
transaction: t
|
||||
}
|
||||
|
||||
if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
|
||||
if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.displayName)
|
||||
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
|
||||
if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support)
|
||||
|
||||
|
@ -17,9 +17,9 @@ async function createUserAccountAndChannel (userToCreate: UserModel, validateUse
|
||||
const userCreated = await userToCreate.save(userOptions)
|
||||
const accountCreated = await createLocalAccountWithoutKeys(userToCreate.username, userToCreate.id, null, t)
|
||||
|
||||
const videoChannelName = `Default ${userCreated.username} channel`
|
||||
const videoChannelDisplayName = `Default ${userCreated.username} channel`
|
||||
const videoChannelInfo = {
|
||||
name: videoChannelName
|
||||
displayName: videoChannelDisplayName
|
||||
}
|
||||
const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t)
|
||||
|
||||
|
@ -14,7 +14,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
|
||||
const actorInstanceCreated = await actorInstance.save({ transaction: t })
|
||||
|
||||
const videoChannelData = {
|
||||
name: videoChannelInfo.name,
|
||||
name: videoChannelInfo.displayName,
|
||||
description: videoChannelInfo.description,
|
||||
support: videoChannelInfo.support,
|
||||
accountId: account.id,
|
||||
|
@ -27,7 +27,7 @@ const listVideoAccountChannelsValidator = [
|
||||
]
|
||||
|
||||
const videoChannelsAddValidator = [
|
||||
body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
|
||||
body('displayName').custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
|
||||
body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
|
||||
body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
|
||||
|
||||
@ -42,7 +42,7 @@ const videoChannelsAddValidator = [
|
||||
|
||||
const videoChannelsUpdateValidator = [
|
||||
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
|
||||
body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
|
||||
body('displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
|
||||
body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
|
||||
body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
|
||||
|
||||
|
@ -25,7 +25,7 @@ import { User } from '../../../../shared/models/users'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
describe('Test videos API validator', function () {
|
||||
describe('Test video channels API validator', function () {
|
||||
const videoChannelPath = '/api/v1/video-channels'
|
||||
let server: ServerInfo
|
||||
let accessTokenUser: string
|
||||
@ -85,7 +85,7 @@ describe('Test videos API validator', function () {
|
||||
|
||||
describe('When adding a video channel', function () {
|
||||
const baseCorrectParams = {
|
||||
name: 'hello',
|
||||
displayName: 'hello',
|
||||
description: 'super description',
|
||||
support: 'super support text'
|
||||
}
|
||||
@ -105,13 +105,13 @@ describe('Test videos API validator', function () {
|
||||
await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
|
||||
})
|
||||
|
||||
it('Should fail without name', async function () {
|
||||
const fields = omit(baseCorrectParams, 'name')
|
||||
it('Should fail without a name', async function () {
|
||||
const fields = omit(baseCorrectParams, 'displayName')
|
||||
await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
|
||||
})
|
||||
|
||||
it('Should fail with a long name', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(25) })
|
||||
const fields = immutableAssign(baseCorrectParams, { displayName: 'super'.repeat(25) })
|
||||
await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
|
||||
})
|
||||
|
||||
@ -138,7 +138,7 @@ describe('Test videos API validator', function () {
|
||||
|
||||
describe('When updating a video channel', function () {
|
||||
const baseCorrectParams = {
|
||||
name: 'hello',
|
||||
displayName: 'hello',
|
||||
description: 'super description'
|
||||
}
|
||||
let path: string
|
||||
@ -168,7 +168,7 @@ describe('Test videos API validator', function () {
|
||||
})
|
||||
|
||||
it('Should fail with a long name', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(25) })
|
||||
const fields = immutableAssign(baseCorrectParams, { displayName: 'super'.repeat(25) })
|
||||
await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
||||
})
|
||||
|
||||
|
@ -59,7 +59,7 @@ describe('Test multiple servers', function () {
|
||||
|
||||
{
|
||||
const videoChannel = {
|
||||
name: 'my channel',
|
||||
displayName: 'my channel',
|
||||
description: 'super channel'
|
||||
}
|
||||
await addVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, videoChannel)
|
||||
|
@ -59,7 +59,7 @@ describe('Test video channels', function () {
|
||||
this.timeout(10000)
|
||||
|
||||
const videoChannel = {
|
||||
name: 'second video channel',
|
||||
displayName: 'second video channel',
|
||||
description: 'super video channel description',
|
||||
support: 'super video channel support text'
|
||||
}
|
||||
@ -125,7 +125,7 @@ describe('Test video channels', function () {
|
||||
this.timeout(5000)
|
||||
|
||||
const videoChannelAttributes = {
|
||||
name: 'video channel updated',
|
||||
displayName: 'video channel updated',
|
||||
description: 'video channel description updated',
|
||||
support: 'video channel support text updated'
|
||||
}
|
||||
|
@ -1,10 +1,5 @@
|
||||
import * as request from 'supertest'
|
||||
|
||||
type VideoChannelAttributes = {
|
||||
name?: string
|
||||
description?: string
|
||||
support?: string
|
||||
}
|
||||
import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared/models/videos'
|
||||
|
||||
function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
|
||||
const path = '/api/v1/video-channels'
|
||||
@ -34,14 +29,14 @@ function getAccountVideoChannelsList (url: string, accountId: number | string, s
|
||||
function addVideoChannel (
|
||||
url: string,
|
||||
token: string,
|
||||
videoChannelAttributesArg: VideoChannelAttributes,
|
||||
videoChannelAttributesArg: VideoChannelCreate,
|
||||
expectedStatus = 200
|
||||
) {
|
||||
const path = '/api/v1/video-channels/'
|
||||
|
||||
// Default attributes
|
||||
let attributes = {
|
||||
name: 'my super video channel',
|
||||
displayName: 'my super video channel',
|
||||
description: 'my super channel description',
|
||||
support: 'my super channel support'
|
||||
}
|
||||
@ -59,13 +54,13 @@ function updateVideoChannel (
|
||||
url: string,
|
||||
token: string,
|
||||
channelId: number | string,
|
||||
attributes: VideoChannelAttributes,
|
||||
attributes: VideoChannelUpdate,
|
||||
expectedStatus = 204
|
||||
) {
|
||||
const body = {}
|
||||
const path = '/api/v1/video-channels/' + channelId
|
||||
|
||||
if (attributes.name) body['name'] = attributes.name
|
||||
if (attributes.displayName) body['displayName'] = attributes.displayName
|
||||
if (attributes.description) body['description'] = attributes.description
|
||||
if (attributes.support) body['support'] = attributes.support
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
export interface VideoChannelCreate {
|
||||
name: string
|
||||
displayName: string
|
||||
description?: string
|
||||
support?: string
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export interface VideoChannelUpdate {
|
||||
name: string
|
||||
displayName: string
|
||||
description?: string
|
||||
support?: string
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user