aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+manage/video-channel-edit/video-channel-update.component.ts7
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-settings.component.ts9
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html46
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss23
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts3
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html33
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss18
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss16
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.ts7
-rw-r--r--client/src/sass/bootstrap.scss2
-rw-r--r--shared/core-utils/common/object.ts5
11 files changed, 91 insertions, 78 deletions
diff --git a/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts b/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts
index 32f6d650d..3326a1505 100644
--- a/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts
+++ b/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts
@@ -13,6 +13,7 @@ import { FormReactiveService } from '@app/shared/shared-forms'
13import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' 13import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
14import { HTMLServerConfig, VideoChannelUpdate } from '@shared/models' 14import { HTMLServerConfig, VideoChannelUpdate } from '@shared/models'
15import { VideoChannelEdit } from './video-channel-edit' 15import { VideoChannelEdit } from './video-channel-edit'
16import { shallowCopy } from '@shared/core-utils'
16 17
17@Component({ 18@Component({
18 selector: 'my-video-channel-update', 19 selector: 'my-video-channel-update',
@@ -118,6 +119,9 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
118 this.notifier.success($localize`Avatar changed.`) 119 this.notifier.success($localize`Avatar changed.`)
119 120
120 this.videoChannel.updateAvatar(data.avatars) 121 this.videoChannel.updateAvatar(data.avatars)
122
123 // So my-actor-avatar component detects changes
124 this.videoChannel = shallowCopy(this.videoChannel)
121 }, 125 },
122 126
123 error: (err: HttpErrorResponse) => genericUploadErrorHandler({ 127 error: (err: HttpErrorResponse) => genericUploadErrorHandler({
@@ -135,6 +139,9 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
135 this.notifier.success($localize`Avatar deleted.`) 139 this.notifier.success($localize`Avatar deleted.`)
136 140
137 this.videoChannel.resetAvatar() 141 this.videoChannel.resetAvatar()
142
143 // So my-actor-avatar component detects changes
144 this.videoChannel = shallowCopy(this.videoChannel)
138 }, 145 },
139 146
140 error: err => this.notifier.error(err.message) 147 error: err => this.notifier.error(err.message)
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
index 577f4a252..a276bb126 100644
--- 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
@@ -3,6 +3,7 @@ import { HttpErrorResponse } from '@angular/common/http'
3import { AfterViewChecked, Component, OnInit } from '@angular/core' 3import { AfterViewChecked, Component, OnInit } from '@angular/core'
4import { AuthService, Notifier, User, UserService } from '@app/core' 4import { AuthService, Notifier, User, UserService } from '@app/core'
5import { genericUploadErrorHandler } from '@app/helpers' 5import { genericUploadErrorHandler } from '@app/helpers'
6import { shallowCopy } from '@shared/core-utils'
6 7
7@Component({ 8@Component({
8 selector: 'my-account-settings', 9 selector: 'my-account-settings',
@@ -44,6 +45,9 @@ export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
44 this.notifier.success($localize`Avatar changed.`) 45 this.notifier.success($localize`Avatar changed.`)
45 46
46 this.user.updateAccountAvatar(data.avatars) 47 this.user.updateAccountAvatar(data.avatars)
48
49 // So my-actor-avatar component detects changes
50 this.user.account = shallowCopy(this.user.account)
47 }, 51 },
48 52
49 error: (err: HttpErrorResponse) => genericUploadErrorHandler({ 53 error: (err: HttpErrorResponse) => genericUploadErrorHandler({
@@ -57,10 +61,13 @@ export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
57 onAvatarDelete () { 61 onAvatarDelete () {
58 this.userService.deleteAvatar() 62 this.userService.deleteAvatar()
59 .subscribe({ 63 .subscribe({
60 next: data => { 64 next: () => {
61 this.notifier.success($localize`Avatar deleted.`) 65 this.notifier.success($localize`Avatar deleted.`)
62 66
63 this.user.updateAccountAvatar() 67 this.user.updateAccountAvatar()
68
69 // So my-actor-avatar component detects changes
70 this.user.account = shallowCopy(this.user.account)
64 }, 71 },
65 72
66 error: (err: HttpErrorResponse) => this.notifier.error(err.message) 73 error: (err: HttpErrorResponse) => this.notifier.error(err.message)
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
index 6459c5ffe..a0f65a3d9 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
@@ -1,23 +1,32 @@
1<div class="actor" *ngIf="actor"> 1<div class="actor" *ngIf="actor">
2 <div class="d-flex"> 2 <div class="position-relative me-3">
3 <my-actor-avatar [actor]="actor" [actorType]="getActorType()" [previewImage]="preview" size="100"></my-actor-avatar> 3 <my-actor-avatar [actor]="actor" [actorType]="getActorType()" [previewImage]="preview" size="100"></my-actor-avatar>
4 4
5 <div class="actor-img-edit-container"> 5 <div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body">
6 6 <my-global-icon iconName="upload"></my-global-icon>
7 <div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body"> 7 <label class="visually-hidden" for="avatarfile" i18n>Upload a new avatar</label>
8 <my-global-icon iconName="upload"></my-global-icon> 8 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
9 <label class="visually-hidden" for="avatarfile" i18n>Upload a new avatar</label> 9 </div>
10 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
11 </div>
12 10
13 <div 11 <div *ngIf="editable && hasAvatar()" ngbDropdown placement="right">
14 *ngIf="editable && hasAvatar()" class="actor-img-edit-button" 12 <div class="actor-img-edit-button" ngbDropdownToggle>
15 #avatarPopover="ngbPopover" [ngbPopover]="avatarEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right"
16 >
17 <my-global-icon iconName="edit"></my-global-icon> 13 <my-global-icon iconName="edit"></my-global-icon>
18 <label class="visually-hidden" for="avatarMenu" i18n>Change your avatar</label> 14 <label class="visually-hidden" for="avatarMenu" i18n>Change your avatar</label>
19 </div> 15 </div>
20 16
17 <div ngbDropdownMenu>
18 <div class="dropdown-item c-hand dropdown-file" [ngbTooltip]="avatarFormat">
19 <my-global-icon iconName="upload"></my-global-icon>
20 <span for="avatarfile" i18n>Upload a new avatar</span>
21 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
22 </div>
23
24 <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
25 <my-global-icon iconName="delete"></my-global-icon>
26 <span i18n>Remove avatar</span>
27 </div>
28 </div>
29
21 </div> 30 </div>
22 </div> 31 </div>
23 32
@@ -27,16 +36,3 @@
27 <div *ngIf="displaySubscribers" i18n class="actor-info-followers">{{ actor.followersCount }} subscribers</div> 36 <div *ngIf="displaySubscribers" i18n class="actor-info-followers">{{ actor.followersCount }} subscribers</div>
28 </div> 37 </div>
29</div> 38</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
38 <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
39 <my-global-icon iconName="delete"></my-global-icon>
40 <span i18n>Remove avatar</span>
41 </div>
42</ng-template>
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
index fd8cd7ffc..01e2131ba 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
@@ -5,10 +5,6 @@
5 display: flex; 5 display: flex;
6} 6}
7 7
8my-actor-avatar {
9 @include margin-right(15px);
10}
11
12.actor-info { 8.actor-info {
13 display: inline-flex; 9 display: inline-flex;
14 flex-direction: column; 10 flex-direction: column;
@@ -16,12 +12,12 @@ my-actor-avatar {
16 12
17.actor-info-display-name { 13.actor-info-display-name {
18 @include peertube-word-wrap; 14 @include peertube-word-wrap;
15 @include font-size(1.25rem);
19 16
20 font-size: 20px;
21 font-weight: $font-bold; 17 font-weight: $font-bold;
22 18
23 @media screen and (max-width: $small-view) { 19 @media screen and (max-width: $small-view) {
24 font-size: 16px; 20 @include font-size(18px);
25 } 21 }
26} 22}
27 23
@@ -35,17 +31,18 @@ my-actor-avatar {
35 padding-bottom: .5rem; 31 padding-bottom: .5rem;
36} 32}
37 33
38.actor-img-edit-container {
39 position: relative;
40 width: 0;
41}
42
43.actor-img-edit-button { 34.actor-img-edit-button {
44 top: 55px;
45 right: 45px;
46 border-radius: 50%; 35 border-radius: 50%;
36
37 position: absolute;
38 bottom: 5px;
39 right: 5px;
47} 40}
48 41
49.dropdown-item { 42.dropdown-item {
50 @include dropdown-with-icon-item; 43 @include dropdown-with-icon-item;
51} 44}
45
46.dropdown-toggle::after {
47 display: none;
48}
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
index b71a3c485..fc925083e 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
@@ -1,7 +1,6 @@
1import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
2import { Notifier, ServerService } from '@app/core' 2import { Notifier, ServerService } from '@app/core'
3import { Account, VideoChannel } from '@app/shared/shared-main' 3import { Account, VideoChannel } from '@app/shared/shared-main'
4import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
5import { getBytes } from '@root-helpers/bytes' 4import { getBytes } from '@root-helpers/bytes'
6import { imageToDataURL } from '@root-helpers/images' 5import { imageToDataURL } from '@root-helpers/images'
7 6
@@ -15,7 +14,6 @@ import { imageToDataURL } from '@root-helpers/images'
15}) 14})
16export class ActorAvatarEditComponent implements OnInit { 15export class ActorAvatarEditComponent implements OnInit {
17 @ViewChild('avatarfileInput') avatarfileInput: ElementRef<HTMLInputElement> 16 @ViewChild('avatarfileInput') avatarfileInput: ElementRef<HTMLInputElement>
18 @ViewChild('avatarPopover') avatarPopover: NgbPopover
19 17
20 @Input() actor: VideoChannel | Account 18 @Input() actor: VideoChannel | Account
21 @Input() editable = true 19 @Input() editable = true
@@ -58,7 +56,6 @@ export class ActorAvatarEditComponent implements OnInit {
58 56
59 const formData = new FormData() 57 const formData = new FormData()
60 formData.append('avatarfile', avatarfile) 58 formData.append('avatarfile', avatarfile)
61 this.avatarPopover?.close()
62 this.avatarChange.emit(formData) 59 this.avatarChange.emit(formData)
63 60
64 if (this.previewImage) { 61 if (this.previewImage) {
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
index f675371d9..d6fe37094 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
@@ -8,26 +8,25 @@
8 <ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container> 8 <ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
9 </div> 9 </div>
10 10
11 <div 11 <div *ngIf="hasBanner()" ngbDropdown placement="right">
12 *ngIf="hasBanner()" class="actor-img-edit-button" 12 <div class="actor-img-edit-button" ngbDropdownToggle>
13 #bannerPopover="ngbPopover" [ngbPopover]="bannerEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right" 13 <my-global-icon iconName="edit"></my-global-icon>
14 > 14 <label for="bannerMenu" i18n>Change your banner</label>
15 <my-global-icon iconName="edit"></my-global-icon> 15 </div>
16 <label for="bannerMenu" i18n>Change your banner</label>
17 </div>
18 </div>
19</div>
20 16
21<ng-template #bannerEditContent> 17 <div ngbDropdownMenu>
22 <div class="dropdown-item c-hand" [ngbTooltip]="bannerFormat" placement="right" container="body"> 18 <div class="dropdown-item c-hand dropdown-file" [ngbTooltip]="bannerFormat">
23 <ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container> 19 <ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
24 </div> 20 </div>
25 21
26 <div class="dropdown-item c-hand" (click)="deleteBanner()" (key.enter)="deleteBanner()"> 22 <div class="dropdown-item c-hand" (click)="deleteBanner()" (key.enter)="deleteBanner()">
27 <my-global-icon iconName="delete"></my-global-icon> 23 <my-global-icon iconName="delete"></my-global-icon>
28 <span i18n>Remove banner</span> 24 <span i18n>Remove banner</span>
25 </div>
26 </div>
27 </div>
29 </div> 28 </div>
30</ng-template> 29</div>
31 30
32<ng-template #uploadNewBanner> 31<ng-template #uploadNewBanner>
33 <my-global-icon iconName="upload"></my-global-icon> 32 <my-global-icon iconName="upload"></my-global-icon>
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
index ec2de2528..b2c64fff7 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
@@ -16,12 +16,28 @@
16 align-items: center; 16 align-items: center;
17} 17}
18 18
19.actor-img-edit-button { 19.dropdown {
20 position: absolute; 20 position: absolute;
21
22 > .actor-img-edit-button {
23 position: relative;
24 }
25}
26
27.actor-img-edit-button {
21 width: auto; 28 width: auto;
29 position: absolute;
22 30
23 label { 31 label {
24 font-weight: $font-semibold; 32 font-weight: $font-semibold;
25 margin-bottom: 0; 33 margin-bottom: 0;
26 } 34 }
27} 35}
36
37.dropdown-item {
38 @include dropdown-with-icon-item;
39}
40
41.dropdown-toggle::after {
42 display: none;
43}
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss b/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss
index b054086e4..9e4ff2654 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss
@@ -1,18 +1,8 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4.actor ::ng-deep .popover-image-info .popover-body { 4.dropdown-file {
5 padding: 0; 5 @include peertube-file;
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} 6}
17 7
18.actor-img-edit-button { 8.actor-img-edit-button {
@@ -22,8 +12,6 @@
22 12
23 display: flex; 13 display: flex;
24 justify-content: center; 14 justify-content: center;
25 margin-top: 10px;
26 margin-bottom: 5px;
27 cursor: pointer; 15 cursor: pointer;
28 16
29 input { 17 input {
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
index 6036123f9..a52e68a17 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
@@ -50,15 +50,15 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
50 ngOnInit () { 50 ngOnInit () {
51 this.buildDefaultAvatarUrl() 51 this.buildDefaultAvatarUrl()
52 52
53 this.buildClasses()
54 this.buildAlt() 53 this.buildAlt()
55 this.buildAvatarUrl() 54 this.buildAvatarUrl()
55 this.buildClasses()
56 } 56 }
57 57
58 ngOnChanges () { 58 ngOnChanges () {
59 this.buildClasses()
60 this.buildAlt() 59 this.buildAlt()
61 this.buildAvatarUrl() 60 this.buildAvatarUrl()
61 this.buildClasses()
62 } 62 }
63 63
64 private buildClasses () { 64 private buildClasses () {
@@ -114,12 +114,13 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
114 114
115 displayImage () { 115 displayImage () {
116 if (this.actorType === 'unlogged') return true 116 if (this.actorType === 'unlogged') return true
117 if (this.previewImage) return true
117 118
118 return !!(this.actor && this.avatarUrl) 119 return !!(this.actor && this.avatarUrl)
119 } 120 }
120 121
121 displayActorInitial () { 122 displayActorInitial () {
122 return this.actor && !this.avatarUrl 123 return !this.displayImage() && this.actor && !this.avatarUrl
123 } 124 }
124 125
125 displayPlaceholder () { 126 displayPlaceholder () {
diff --git a/client/src/sass/bootstrap.scss b/client/src/sass/bootstrap.scss
index 3b847c75b..4d956d652 100644
--- a/client/src/sass/bootstrap.scss
+++ b/client/src/sass/bootstrap.scss
@@ -30,7 +30,7 @@
30@import 'bootstrap/scss/helpers'; 30@import 'bootstrap/scss/helpers';
31@import 'bootstrap/scss/utilities/api'; 31@import 'bootstrap/scss/utilities/api';
32 32
33:root { 33body {
34 --bs-border-color-translucent: #{pvar(--inputBorderColor)}; 34 --bs-border-color-translucent: #{pvar(--inputBorderColor)};
35} 35}
36 36
diff --git a/shared/core-utils/common/object.ts b/shared/core-utils/common/object.ts
index 2330c9403..7f1f147f4 100644
--- a/shared/core-utils/common/object.ts
+++ b/shared/core-utils/common/object.ts
@@ -41,9 +41,14 @@ function sortObjectComparator (key: string, order: 'asc' | 'desc') {
41 } 41 }
42} 42}
43 43
44function shallowCopy <T> (o: T): T {
45 return Object.assign(Object.create(Object.getPrototypeOf(o)), o)
46}
47
44export { 48export {
45 pick, 49 pick,
46 omit, 50 omit,
47 getKeys, 51 getKeys,
52 shallowCopy,
48 sortObjectComparator 53 sortObjectComparator
49} 54}