aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/shared-actor-image
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared/shared-actor-image')
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html41
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss54
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts73
-rw-r--r--client/src/app/shared/shared-actor-image/actor-banner-edit.component.html34
-rw-r--r--client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss27
-rw-r--r--client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts65
-rw-r--r--client/src/app/shared/shared-actor-image/actor-image-edit.scss35
-rw-r--r--client/src/app/shared/shared-actor-image/index.ts1
-rw-r--r--client/src/app/shared/shared-actor-image/shared-actor-image.module.ts29
9 files changed, 359 insertions, 0 deletions
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html
new file mode 100644
index 000000000..10f2ef262
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html
@@ -0,0 +1,41 @@
1<div class="actor" *ngIf="actor">
2 <div class="d-flex">
3 <img [ngClass]="{ channel: isChannel() }" [src]="actor.avatarUrl" alt="Avatar" />
4
5 <div class="actor-img-edit-container">
6
7 <div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body">
8 <my-global-icon iconName="upload"></my-global-icon>
9 <label class="sr-only" for="avatarfile" i18n>Upload a new avatar</label>
10 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
11 </div>
12
13 <div
14 *ngIf="editable && hasAvatar()" class="actor-img-edit-button"
15 #avatarPopover="ngbPopover" [ngbPopover]="avatarEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right"
16 >
17 <my-global-icon iconName="edit"></my-global-icon>
18 <label class="sr-only" for="avatarMenu" i18n>Change your avatar</label>
19 </div>
20
21 </div>
22 </div>
23
24 <div class="actor-info">
25 <div class="actor-info-display-name">{{ actor.displayName }}</div>
26 <div *ngIf="displayUsername" class="actor-info-username">{{ actor.name }}</div>
27 <div *ngIf="displaySubscribers" i18n class="actor-info-followers">{{ actor.followersCount }} subscribers</div>
28 </div>
29</div>
30
31<ng-template #avatarEditContent>
32 <div class="dropdown-item c-hand" [ngbTooltip]="avatarFormat" placement="right" container="body">
33 <my-global-icon iconName="upload"></my-global-icon>
34 <span for="avatarfile" i18n>Upload a new avatar</span>
35 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
36 </div>
37 <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
38 <my-global-icon iconName="delete"></my-global-icon>
39 <span i18n>Remove avatar</span>
40 </div>
41</ng-template>
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss
new file mode 100644
index 000000000..8b0172315
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss
@@ -0,0 +1,54 @@
1@import '_variables';
2@import '_mixins';
3
4.actor {
5 display: flex;
6
7 img {
8 margin-right: 15px;
9
10 &:not(.channel) {
11 @include avatar(100px);
12 }
13
14 &.channel {
15 @include channel-avatar(100px);
16 }
17 }
18
19 .actor-info {
20 display: inline-flex;
21 flex-direction: column;
22
23 .actor-info-display-name {
24 font-size: 20px;
25 font-weight: $font-bold;
26
27 @media screen and (max-width: $small-view) {
28 font-size: 16px;
29 }
30 }
31
32 .actor-info-username {
33 position: relative;
34 font-size: 14px;
35 color: pvar(--greyForegroundColor);
36 }
37
38 .actor-info-followers {
39 font-size: 15px;
40 padding-bottom: .5rem;
41 }
42 }
43}
44
45.actor-img-edit-container {
46 position: relative;
47 width: 0;
48}
49
50.actor-img-edit-button {
51 top: 55px;
52 right: 45px;
53 border-radius: 50%;
54}
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts
new file mode 100644
index 000000000..6f76172e9
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts
@@ -0,0 +1,73 @@
1import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
2import { Notifier, ServerService } from '@app/core'
3import { Account, VideoChannel } from '@app/shared/shared-main'
4import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
5import { getBytes } from '@root-helpers/bytes'
6
7@Component({
8 selector: 'my-actor-avatar-edit',
9 templateUrl: './actor-avatar-edit.component.html',
10 styleUrls: [
11 './actor-image-edit.scss',
12 './actor-avatar-edit.component.scss'
13 ]
14})
15export class ActorAvatarEditComponent implements OnInit {
16 @ViewChild('avatarfileInput') avatarfileInput: ElementRef<HTMLInputElement>
17 @ViewChild('avatarPopover') avatarPopover: NgbPopover
18
19 @Input() actor: VideoChannel | Account
20 @Input() editable = true
21 @Input() displaySubscribers = true
22 @Input() displayUsername = true
23
24 @Output() avatarChange = new EventEmitter<FormData>()
25 @Output() avatarDelete = new EventEmitter<void>()
26
27 avatarFormat = ''
28 maxAvatarSize = 0
29 avatarExtensions = ''
30
31 constructor (
32 private serverService: ServerService,
33 private notifier: Notifier
34 ) { }
35
36 ngOnInit (): void {
37 this.serverService.getConfig()
38 .subscribe(config => {
39 this.maxAvatarSize = config.avatar.file.size.max
40 this.avatarExtensions = config.avatar.file.extensions.join(', ')
41
42 this.avatarFormat = `${$localize`max size`}: 192*192px, ` +
43 `${getBytes(this.maxAvatarSize)} ${$localize`extensions`}: ${this.avatarExtensions}`
44 })
45 }
46
47 onAvatarChange (input: HTMLInputElement) {
48 this.avatarfileInput = new ElementRef(input)
49
50 const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ]
51 if (avatarfile.size > this.maxAvatarSize) {
52 this.notifier.error('Error', $localize`This image is too large.`)
53 return
54 }
55
56 const formData = new FormData()
57 formData.append('avatarfile', avatarfile)
58 this.avatarPopover?.close()
59 this.avatarChange.emit(formData)
60 }
61
62 deleteAvatar () {
63 this.avatarDelete.emit()
64 }
65
66 hasAvatar () {
67 return !!this.actor.avatar
68 }
69
70 isChannel () {
71 return !!(this.actor as VideoChannel).ownerAccount
72 }
73}
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html
new file mode 100644
index 000000000..eb1b66422
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html
@@ -0,0 +1,34 @@
1<div class="actor" *ngIf="actor">
2 <div class="actor-img-edit-container">
3 <div class="banner-placeholder">
4 <img *ngIf="hasBanner()" [src]="actor.bannerUrl" alt="Banner" />
5 </div>
6
7 <div *ngIf="!hasBanner()" class="actor-img-edit-button" [ngbTooltip]="bannerFormat" placement="right" container="body">
8 <my-global-icon iconName="upload"></my-global-icon>
9 <label for="bannerfile" i18n>Upload a new banner</label>
10 <input #bannerfileInput type="file" name="bannerfile" id="bannerfile" [accept]="bannerExtensions" (change)="onBannerChange(bannerfileInput)"/>
11 </div>
12
13 <div
14 *ngIf="hasBanner()" class="actor-img-edit-button"
15 #bannerPopover="ngbPopover" [ngbPopover]="bannerEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right"
16 >
17 <my-global-icon iconName="edit"></my-global-icon>
18 <label for="bannerMenu" i18n>Change your banner</label>
19 </div>
20 </div>
21</div>
22
23<ng-template #bannerEditContent>
24 <div class="dropdown-item c-hand" [ngbTooltip]="bannerFormat" placement="right" container="body">
25 <my-global-icon iconName="upload"></my-global-icon>
26 <span for="bannerfile" i18n>Upload a new banner</span>
27 <input #bannerfileInput type="file" name="bannerfile" id="bannerfile" [accept]="bannerExtensions" (change)="onBannerChange(bannerfileInput)"/>
28 </div>
29
30 <div class="dropdown-item c-hand" (click)="deleteBanner()" (key.enter)="deleteBanner()">
31 <my-global-icon iconName="delete"></my-global-icon>
32 <span i18n>Remove banner</span>
33 </div>
34</ng-template>
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss
new file mode 100644
index 000000000..23606f871
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss
@@ -0,0 +1,27 @@
1@import '_variables';
2@import '_mixins';
3
4.banner-placeholder {
5 @include block-ratio('> div, > img', $banner-inverted-ratio);
6}
7
8.banner-placeholder {
9 background-color: pvar(--greyBackgroundColor);
10}
11
12.actor-img-edit-container {
13 position: relative;
14 display: flex;
15 justify-content: center;
16 align-items: center;
17}
18
19.actor-img-edit-button {
20 position: absolute;
21 width: auto;
22
23 label {
24 font-weight: $font-semibold;
25 margin-bottom: 0;
26 }
27}
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts
new file mode 100644
index 000000000..b92ecef4b
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts
@@ -0,0 +1,65 @@
1import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'
2import { Notifier, ServerService } from '@app/core'
3import { VideoChannel } from '@app/shared/shared-main'
4import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
5import { getBytes } from '@root-helpers/bytes'
6
7@Component({
8 selector: 'my-actor-banner-edit',
9 templateUrl: './actor-banner-edit.component.html',
10 styleUrls: [
11 './actor-image-edit.scss',
12 './actor-banner-edit.component.scss'
13 ]
14})
15export class ActorBannerEditComponent implements OnInit {
16 @ViewChild('bannerfileInput') bannerfileInput: ElementRef<HTMLInputElement>
17 @ViewChild('bannerPopover') bannerPopover: NgbPopover
18
19 @Input() actor: VideoChannel
20
21 @Output() bannerChange = new EventEmitter<FormData>()
22 @Output() bannerDelete = new EventEmitter<void>()
23
24 bannerFormat = ''
25 maxBannerSize = 0
26 bannerExtensions = ''
27
28 constructor (
29 private serverService: ServerService,
30 private notifier: Notifier
31 ) { }
32
33 ngOnInit (): void {
34 this.serverService.getConfig()
35 .subscribe(config => {
36 this.maxBannerSize = config.banner.file.size.max
37 this.bannerExtensions = config.banner.file.extensions.join(', ')
38
39 this.bannerFormat = $localize`maxsize: ${getBytes(this.maxBannerSize)}, extensions: ${this.bannerExtensions}`
40 })
41 }
42
43 onBannerChange (input: HTMLInputElement) {
44 this.bannerfileInput = new ElementRef(input)
45
46 const bannerfile = this.bannerfileInput.nativeElement.files[ 0 ]
47 if (bannerfile.size > this.maxBannerSize) {
48 this.notifier.error('Error', $localize`This image is too large.`)
49 return
50 }
51
52 const formData = new FormData()
53 formData.append('bannerfile', bannerfile)
54 this.bannerPopover?.close()
55 this.bannerChange.emit(formData)
56 }
57
58 deleteBanner () {
59 this.bannerDelete.emit()
60 }
61
62 hasBanner () {
63 return !!this.actor.bannerUrl
64 }
65}
diff --git a/client/src/app/shared/shared-actor-image/actor-image-edit.scss b/client/src/app/shared/shared-actor-image/actor-image-edit.scss
new file mode 100644
index 000000000..918955a89
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-image-edit.scss
@@ -0,0 +1,35 @@
1@import '_variables';
2@import '_mixins';
3
4.actor ::ng-deep .popover-image-info .popover-body {
5 padding: 0;
6
7 .dropdown-item {
8 padding: 6px 10px;
9 border-radius: 4px;
10
11 &:first-child {
12 @include peertube-file;
13 display: block;
14 }
15 }
16}
17
18.actor-img-edit-button {
19 @include peertube-button-file(21px);
20 @include button-with-icon(19px);
21 @include orange-button;
22
23 margin-top: 10px;
24 margin-bottom: 5px;
25 cursor: pointer;
26
27 input {
28 width: 30px;
29 height: 30px;
30 }
31
32 my-global-icon {
33 right: 7px;
34 }
35}
diff --git a/client/src/app/shared/shared-actor-image/index.ts b/client/src/app/shared/shared-actor-image/index.ts
new file mode 100644
index 000000000..18a9038eb
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/index.ts
@@ -0,0 +1 @@
export * from './shared-actor-image.module'
diff --git a/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts
new file mode 100644
index 000000000..6044f9925
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts
@@ -0,0 +1,29 @@
1
2import { CommonModule } from '@angular/common'
3import { NgModule } from '@angular/core'
4import { SharedGlobalIconModule } from '../shared-icons'
5import { SharedMainModule } from '../shared-main'
6import { ActorAvatarEditComponent } from './actor-avatar-edit.component'
7import { ActorBannerEditComponent } from './actor-banner-edit.component'
8
9@NgModule({
10 imports: [
11 CommonModule,
12
13 SharedMainModule,
14 SharedGlobalIconModule
15 ],
16
17 declarations: [
18 ActorAvatarEditComponent,
19 ActorBannerEditComponent
20 ],
21
22 exports: [
23 ActorAvatarEditComponent,
24 ActorBannerEditComponent
25 ],
26
27 providers: [ ]
28})
29export class SharedActorImageModule { }