diff options
author | Chocobozzz <me@florianbigard.com> | 2020-11-12 15:28:54 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-11-13 12:02:21 +0100 |
commit | 17119e4a546522468878cf115558b17949ab50d0 (patch) | |
tree | 3f130cfd7fdccf5aeeac9beee941750590239047 /client/src/app/+my-library/my-ownership | |
parent | b4bc269e5517849b5b89052f0c1a2c01b6f65089 (diff) | |
download | PeerTube-17119e4a546522468878cf115558b17949ab50d0.tar.gz PeerTube-17119e4a546522468878cf115558b17949ab50d0.tar.zst PeerTube-17119e4a546522468878cf115558b17949ab50d0.zip |
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
Diffstat (limited to 'client/src/app/+my-library/my-ownership')
6 files changed, 364 insertions, 0 deletions
diff --git a/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html new file mode 100644 index 000000000..def1cbab6 --- /dev/null +++ b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html | |||
@@ -0,0 +1,36 @@ | |||
1 | <ng-template #modal let-close="close" let-dismiss="dismiss"> | ||
2 | <div class="modal-header"> | ||
3 | <h1 i18n class="modal-title">Accept ownership</h1> | ||
4 | |||
5 | <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="dismiss()"></my-global-icon> | ||
6 | </div> | ||
7 | |||
8 | <div class="modal-body" [formGroup]="form"> | ||
9 | <div class="form-group"> | ||
10 | <label i18n for="channel">Select a channel to receive the video</label> | ||
11 | <div class="peertube-select-container"> | ||
12 | <select formControlName="channel" id="channel" class="form-control"> | ||
13 | <option i18n value="undefined" disabled>Channel that will receive the video</option> | ||
14 | <option *ngFor="let channel of videoChannels" [value]="channel.id">{{ channel.displayName }} | ||
15 | </option> | ||
16 | </select> | ||
17 | </div> | ||
18 | <div *ngIf="formErrors.channel" class="form-error">{{ formErrors.channel }}</div> | ||
19 | </div> | ||
20 | </div> | ||
21 | |||
22 | <div class="modal-footer inputs"> | ||
23 | <div class="inputs"> | ||
24 | <input | ||
25 | type="button" role="button" i18n-value value="Cancel" class="action-button action-button-cancel" | ||
26 | (click)="dismiss()" (key.enter)="dismiss()" | ||
27 | > | ||
28 | |||
29 | <input | ||
30 | type="submit" i18n-value value="Accept" class="action-button-submit" | ||
31 | [disabled]="!form.valid" | ||
32 | (click)="close()" | ||
33 | > | ||
34 | </div> | ||
35 | </div> | ||
36 | </ng-template> | ||
diff --git a/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.scss b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.scss new file mode 100644 index 000000000..c7357f62d --- /dev/null +++ b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.scss | |||
@@ -0,0 +1,14 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | select { | ||
5 | display: block; | ||
6 | } | ||
7 | |||
8 | .peertube-select-container { | ||
9 | @include peertube-select-container(350px); | ||
10 | } | ||
11 | |||
12 | .form-group { | ||
13 | margin: 20px 0; | ||
14 | } \ No newline at end of file | ||
diff --git a/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts new file mode 100644 index 000000000..587a455f0 --- /dev/null +++ b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts | |||
@@ -0,0 +1,72 @@ | |||
1 | import { Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | ||
2 | import { AuthService, Notifier } from '@app/core' | ||
3 | import { OWNERSHIP_CHANGE_CHANNEL_VALIDATOR } from '@app/shared/form-validators/video-ownership-change-validators' | ||
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | ||
5 | import { VideoChannelService, VideoOwnershipService } from '@app/shared/shared-main' | ||
6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | ||
7 | import { VideoChangeOwnership, VideoChannel } from '@shared/models' | ||
8 | |||
9 | @Component({ | ||
10 | selector: 'my-accept-ownership', | ||
11 | templateUrl: './my-accept-ownership.component.html', | ||
12 | styleUrls: [ './my-accept-ownership.component.scss' ] | ||
13 | }) | ||
14 | export class MyAcceptOwnershipComponent extends FormReactive implements OnInit { | ||
15 | @Output() accepted = new EventEmitter<void>() | ||
16 | |||
17 | @ViewChild('modal', { static: true }) modal: ElementRef | ||
18 | |||
19 | videoChangeOwnership: VideoChangeOwnership | undefined = undefined | ||
20 | |||
21 | videoChannels: VideoChannel[] | ||
22 | |||
23 | error: string = null | ||
24 | |||
25 | constructor ( | ||
26 | protected formValidatorService: FormValidatorService, | ||
27 | private videoOwnershipService: VideoOwnershipService, | ||
28 | private notifier: Notifier, | ||
29 | private authService: AuthService, | ||
30 | private videoChannelService: VideoChannelService, | ||
31 | private modalService: NgbModal | ||
32 | ) { | ||
33 | super() | ||
34 | } | ||
35 | |||
36 | ngOnInit () { | ||
37 | this.videoChannels = [] | ||
38 | |||
39 | this.videoChannelService.listAccountVideoChannels(this.authService.getUser().account) | ||
40 | .subscribe(videoChannels => this.videoChannels = videoChannels.data) | ||
41 | |||
42 | this.buildForm({ | ||
43 | channel: OWNERSHIP_CHANGE_CHANNEL_VALIDATOR | ||
44 | }) | ||
45 | } | ||
46 | |||
47 | show (videoChangeOwnership: VideoChangeOwnership) { | ||
48 | this.videoChangeOwnership = videoChangeOwnership | ||
49 | this.modalService | ||
50 | .open(this.modal, { centered: true }) | ||
51 | .result | ||
52 | .then(() => this.acceptOwnership()) | ||
53 | .catch(() => this.videoChangeOwnership = undefined) | ||
54 | } | ||
55 | |||
56 | acceptOwnership () { | ||
57 | const channel = this.form.value['channel'] | ||
58 | |||
59 | const videoChangeOwnership = this.videoChangeOwnership | ||
60 | this.videoOwnershipService | ||
61 | .acceptOwnership(videoChangeOwnership.id, { channelId: channel }) | ||
62 | .subscribe( | ||
63 | () => { | ||
64 | this.notifier.success($localize`Ownership accepted`) | ||
65 | if (this.accepted) this.accepted.emit() | ||
66 | this.videoChangeOwnership = undefined | ||
67 | }, | ||
68 | |||
69 | err => this.notifier.error(err.message) | ||
70 | ) | ||
71 | } | ||
72 | } | ||
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.html b/client/src/app/+my-library/my-ownership/my-ownership.component.html new file mode 100644 index 000000000..6bf562986 --- /dev/null +++ b/client/src/app/+my-library/my-ownership/my-ownership.component.html | |||
@@ -0,0 +1,90 @@ | |||
1 | <h1> | ||
2 | <my-global-icon iconName="download" aria-hidden="true"></my-global-icon> | ||
3 | <ng-container i18n>My ownership changes</ng-container> | ||
4 | </h1> | ||
5 | |||
6 | <p-table | ||
7 | [value]="videoChangeOwnerships" | ||
8 | [lazy]="true" | ||
9 | [paginator]="totalRecords > 0" | ||
10 | [totalRecords]="totalRecords" | ||
11 | [rows]="rowsPerPage" | ||
12 | [sortField]="sort.field" | ||
13 | [sortOrder]="sort.order" | ||
14 | (onLazyLoad)="loadLazy($event)" | ||
15 | > | ||
16 | <ng-template pTemplate="header"> | ||
17 | <tr> | ||
18 | <th style="width: 150px;" i18n>Actions</th> | ||
19 | <th style="width: 35%;" i18n>Initiator</th> | ||
20 | <th style="width: 65%;" i18n>Video</th> | ||
21 | <th style="width: 150px;" i18n pSortableColumn="createdAt"> | ||
22 | Created | ||
23 | <p-sortIcon field="createdAt"></p-sortIcon> | ||
24 | </th> | ||
25 | <th style="width: 100px;" i18n>Status</th> | ||
26 | </tr> | ||
27 | </ng-template> | ||
28 | |||
29 | <ng-template pTemplate="body" let-videoChangeOwnership> | ||
30 | <tr> | ||
31 | <td class="action-cell"> | ||
32 | <ng-container *ngIf="videoChangeOwnership.status === 'WAITING'"> | ||
33 | <my-button i18n-title title="Accept" icon="tick" (click)="openAcceptModal(videoChangeOwnership)"></my-button> | ||
34 | <my-button i18n-title title="Refuse" icon="cross" (click)="refuse(videoChangeOwnership)"></my-button> | ||
35 | </ng-container> | ||
36 | </td> | ||
37 | <td> | ||
38 | <a [href]="videoChangeOwnership.initiatorAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> | ||
39 | <div class="chip two-lines"> | ||
40 | <img | ||
41 | class="avatar" | ||
42 | [src]="videoChangeOwnership.initiatorAccount.avatar?.path" | ||
43 | (error)="switchToDefaultAvatar($event)" | ||
44 | alt="Avatar" | ||
45 | > | ||
46 | <div> | ||
47 | {{ videoChangeOwnership.initiatorAccount.displayName }} | ||
48 | <span class="text-muted">{{ videoChangeOwnership.initiatorAccount.nameWithHost }}</span> | ||
49 | </div> | ||
50 | </div> | ||
51 | </a> | ||
52 | </td> | ||
53 | |||
54 | <td> | ||
55 | <a [href]="videoChangeOwnership.video.url" class="video-table-video-link" [title]="videoChangeOwnership.video.name" target="_blank" rel="noopener noreferrer"> | ||
56 | <div class="video-table-video"> | ||
57 | <div class="video-table-video-image"> | ||
58 | <img [src]="videoChangeOwnership.video.thumbnailPath"> | ||
59 | </div> | ||
60 | <div class="video-table-video-text"> | ||
61 | <div> | ||
62 | {{ videoChangeOwnership.video.name }} | ||
63 | </div> | ||
64 | <div class="text-muted">by {{ videoChangeOwnership.video.channel?.displayName }} </div> | ||
65 | </div> | ||
66 | </div> | ||
67 | </a> | ||
68 | </td> | ||
69 | |||
70 | <td>{{ videoChangeOwnership.createdAt | date: 'short' }}</td> | ||
71 | |||
72 | <td> | ||
73 | <span class="badge" | ||
74 | [ngClass]="getStatusClass(videoChangeOwnership.status)">{{ videoChangeOwnership.status }}</span> | ||
75 | </td> | ||
76 | </tr> | ||
77 | </ng-template> | ||
78 | |||
79 | <ng-template pTemplate="emptymessage"> | ||
80 | <tr> | ||
81 | <td colspan="6"> | ||
82 | <div class="no-results"> | ||
83 | <ng-container i18n>No ownership change request found.</ng-container> | ||
84 | </div> | ||
85 | </td> | ||
86 | </tr> | ||
87 | </ng-template> | ||
88 | </p-table> | ||
89 | |||
90 | <my-accept-ownership #myAcceptOwnershipComponent (accepted)="accepted()"></my-accept-ownership> | ||
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.scss b/client/src/app/+my-library/my-ownership/my-ownership.component.scss new file mode 100644 index 000000000..7cac9c9f3 --- /dev/null +++ b/client/src/app/+my-library/my-ownership/my-ownership.component.scss | |||
@@ -0,0 +1,71 @@ | |||
1 | @import 'miniature'; | ||
2 | @import 'mixins'; | ||
3 | |||
4 | .chip { | ||
5 | @include chip; | ||
6 | } | ||
7 | |||
8 | .badge { | ||
9 | @include table-badge; | ||
10 | } | ||
11 | |||
12 | .video-table-video { | ||
13 | display: inline-flex; | ||
14 | |||
15 | .video-table-video-image { | ||
16 | @include miniature-thumbnail; | ||
17 | |||
18 | $image-height: 45px; | ||
19 | |||
20 | height: $image-height; | ||
21 | width: #{(16/9) * $image-height}; | ||
22 | margin-right: 0.5rem; | ||
23 | border-radius: 2px; | ||
24 | border: none; | ||
25 | background: transparent; | ||
26 | display: inline-flex; | ||
27 | justify-content: center; | ||
28 | align-items: center; | ||
29 | position: relative; | ||
30 | |||
31 | img { | ||
32 | height: 100%; | ||
33 | width: 100%; | ||
34 | border-radius: 2px; | ||
35 | } | ||
36 | |||
37 | span { | ||
38 | color: pvar(--inputPlaceholderColor); | ||
39 | } | ||
40 | |||
41 | .video-table-video-image-label { | ||
42 | @include static-thumbnail-overlay; | ||
43 | position: absolute; | ||
44 | border-radius: 3px; | ||
45 | font-size: 10px; | ||
46 | padding: 0 3px; | ||
47 | line-height: 1.3; | ||
48 | bottom: 2px; | ||
49 | right: 2px; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | .video-table-video-text { | ||
54 | display: inline-flex; | ||
55 | flex-direction: column; | ||
56 | justify-content: center; | ||
57 | font-size: 90%; | ||
58 | color: pvar(--mainForegroundColor); | ||
59 | line-height: 1rem; | ||
60 | |||
61 | div .glyphicon { | ||
62 | font-size: 80%; | ||
63 | color: gray; | ||
64 | margin-left: 0.1rem; | ||
65 | } | ||
66 | |||
67 | div + div { | ||
68 | font-size: 80%; | ||
69 | } | ||
70 | } | ||
71 | } | ||
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.ts b/client/src/app/+my-library/my-ownership/my-ownership.component.ts new file mode 100644 index 000000000..e1aca65f6 --- /dev/null +++ b/client/src/app/+my-library/my-ownership/my-ownership.component.ts | |||
@@ -0,0 +1,81 @@ | |||
1 | import { SortMeta } from 'primeng/api' | ||
2 | import { Component, OnInit, ViewChild } from '@angular/core' | ||
3 | import { Notifier, RestPagination, RestTable } from '@app/core' | ||
4 | import { Account, Actor, VideoOwnershipService } from '@app/shared/shared-main' | ||
5 | import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '@shared/models' | ||
6 | import { MyAcceptOwnershipComponent } from './my-accept-ownership/my-accept-ownership.component' | ||
7 | |||
8 | @Component({ | ||
9 | templateUrl: './my-ownership.component.html', | ||
10 | styleUrls: [ './my-ownership.component.scss' ] | ||
11 | }) | ||
12 | export class MyOwnershipComponent extends RestTable implements OnInit { | ||
13 | videoChangeOwnerships: VideoChangeOwnership[] = [] | ||
14 | totalRecords = 0 | ||
15 | sort: SortMeta = { field: 'createdAt', order: -1 } | ||
16 | pagination: RestPagination = { count: this.rowsPerPage, start: 0 } | ||
17 | |||
18 | @ViewChild('myAcceptOwnershipComponent', { static: true }) myAccountAcceptOwnershipComponent: MyAcceptOwnershipComponent | ||
19 | |||
20 | constructor ( | ||
21 | private notifier: Notifier, | ||
22 | private videoOwnershipService: VideoOwnershipService | ||
23 | ) { | ||
24 | super() | ||
25 | } | ||
26 | |||
27 | ngOnInit () { | ||
28 | this.initialize() | ||
29 | } | ||
30 | |||
31 | getIdentifier () { | ||
32 | return 'MyOwnershipComponent' | ||
33 | } | ||
34 | |||
35 | getStatusClass (status: VideoChangeOwnershipStatus) { | ||
36 | switch (status) { | ||
37 | case VideoChangeOwnershipStatus.ACCEPTED: | ||
38 | return 'badge-green' | ||
39 | case VideoChangeOwnershipStatus.REFUSED: | ||
40 | return 'badge-red' | ||
41 | default: | ||
42 | return 'badge-yellow' | ||
43 | } | ||
44 | } | ||
45 | |||
46 | switchToDefaultAvatar ($event: Event) { | ||
47 | ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL() | ||
48 | } | ||
49 | |||
50 | openAcceptModal (videoChangeOwnership: VideoChangeOwnership) { | ||
51 | this.myAccountAcceptOwnershipComponent.show(videoChangeOwnership) | ||
52 | } | ||
53 | |||
54 | accepted () { | ||
55 | this.loadData() | ||
56 | } | ||
57 | |||
58 | refuse (videoChangeOwnership: VideoChangeOwnership) { | ||
59 | this.videoOwnershipService.refuseOwnership(videoChangeOwnership.id) | ||
60 | .subscribe( | ||
61 | () => this.loadData(), | ||
62 | err => this.notifier.error(err.message) | ||
63 | ) | ||
64 | } | ||
65 | |||
66 | protected loadData () { | ||
67 | return this.videoOwnershipService.getOwnershipChanges(this.pagination, this.sort) | ||
68 | .subscribe( | ||
69 | resultList => { | ||
70 | this.videoChangeOwnerships = resultList.data.map(change => ({ | ||
71 | ...change, | ||
72 | initiatorAccount: new Account(change.initiatorAccount), | ||
73 | nextOwnerAccount: new Account(change.nextOwnerAccount) | ||
74 | })) | ||
75 | this.totalRecords = resultList.total | ||
76 | }, | ||
77 | |||
78 | err => this.notifier.error(err.message) | ||
79 | ) | ||
80 | } | ||
81 | } | ||