]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Add AccountAvatarComponent (#3965)
authorkontrollanten <6680299+kontrollanten@users.noreply.github.com>
Thu, 15 Apr 2021 08:01:27 +0000 (10:01 +0200)
committerGitHub <noreply@github.com>
Thu, 15 Apr 2021 08:01:27 +0000 (10:01 +0200)
* refactor(client): create account-avatar component

* continue implement account-avatar

* fix review comments

45 files changed:
client/src/app/+accounts/accounts.component.html
client/src/app/+accounts/accounts.module.ts
client/src/app/+admin/admin.module.ts
client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html
client/src/app/+admin/users/user-list/user-list.component.html
client/src/app/+admin/users/user-list/user-list.component.ts
client/src/app/+my-account/my-account.module.ts
client/src/app/+my-library/my-library.module.ts
client/src/app/+my-library/my-ownership/my-ownership.component.html
client/src/app/+my-library/my-ownership/my-ownership.component.ts
client/src/app/+video-channels/video-channels.component.html
client/src/app/+video-channels/video-channels.module.ts
client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts
client/src/app/+videos/+video-watch/comment/video-comment.component.html
client/src/app/+videos/+video-watch/comment/video-comment.component.scss
client/src/app/+videos/+video-watch/comment/video-comment.component.ts
client/src/app/+videos/+video-watch/video-avatar-channel.component.html
client/src/app/+videos/+video-watch/video-watch.module.ts
client/src/app/app.module.ts
client/src/app/core/users/user.model.ts
client/src/app/menu/menu.component.html
client/src/app/menu/menu.component.scss
client/src/app/shared/shared-abuse-list/abuse-details.component.html
client/src/app/shared/shared-abuse-list/abuse-details.component.ts
client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts
client/src/app/shared/shared-account-avatar/account-avatar.component.html [new file with mode: 0644]
client/src/app/shared/shared-account-avatar/account-avatar.component.scss [new file with mode: 0644]
client/src/app/shared/shared-account-avatar/account-avatar.component.ts [new file with mode: 0644]
client/src/app/shared/shared-account-avatar/index.ts [new file with mode: 0644]
client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts [new file with mode: 0644]
client/src/app/shared/shared-main/video/video.model.ts
client/src/app/shared/shared-moderation/account-blocklist.component.html
client/src/app/shared/shared-moderation/account-blocklist.component.ts
client/src/app/shared/shared-moderation/shared-moderation.module.ts
client/src/app/shared/shared-video-comment/video-comment.model.ts
client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts
client/src/app/shared/shared-video-miniature/video-miniature.component.html
client/src/app/shared/shared-video-miniature/video-miniature.component.scss
client/src/app/shared/shared-video-miniature/video-miniature.component.ts
client/src/sass/include/_actor.scss

index 1b2c113116dd66be404c649baa3ecc710722f6cd..ea7a317eb63780f64cb08c6bbe8a352ecad7eb79 100644 (file)
@@ -2,7 +2,7 @@
   <div class="account-info">
 
     <div class="account-avatar-row">
-      <img class="account-avatar" [src]="account.avatarUrl" alt="Avatar" />
+      <my-account-avatar [account]="account" size="120"></my-account-avatar>
 
       <div>
         <div class="section-label" i18n>PEERTUBE ACCOUNT</div>
index 3354b4189f8650ee5a242e19489d0e6b10cc5771..22cdd0642550d8fe99b9e35d1e4bcc81bd7306cb 100644 (file)
@@ -10,6 +10,7 @@ import { AccountVideoChannelsComponent } from './account-video-channels/account-
 import { AccountVideosComponent } from './account-videos/account-videos.component'
 import { AccountsRoutingModule } from './accounts-routing.module'
 import { AccountsComponent } from './accounts.component'
+import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module'
 
 @NgModule({
   imports: [
@@ -20,7 +21,8 @@ import { AccountsComponent } from './accounts.component'
     SharedUserSubscriptionModule,
     SharedModerationModule,
     SharedVideoMiniatureModule,
-    SharedGlobalIconModule
+    SharedGlobalIconModule,
+    SharedAccountAvatarModule
   ],
 
   declarations: [
index bac65c88e6be858a1f63aaf846d0176f587e410b..8d1c3eadbd1483748bafb0d74434eda2f7f9f65c 100644 (file)
@@ -39,6 +39,7 @@ import { JobService, LogsComponent, LogsService, SystemComponent } from './syste
 import { DebugComponent, DebugService } from './system/debug'
 import { JobsComponent } from './system/jobs/jobs.component'
 import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersComponent, UserUpdateComponent } from './users'
+import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module'
 
 @NgModule({
   imports: [
@@ -50,6 +51,7 @@ import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersCom
     SharedGlobalIconModule,
     SharedAbuseListModule,
     SharedVideoCommentModule,
+    SharedAccountAvatarModule,
     SharedActorImageModule,
 
     TableModule,
index 128f4962dbac6d7aac92d0a3d51e7ef101632f50..f5cf93adb8ab48f57e992f77b463c30c6d16a88f 100644 (file)
       <td>
         <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
           <div class="chip two-lines">
-            <img
-              class="avatar"
-              [src]="accountBlock.blockedAccount.avatar?.path"
-              (error)="switchToDefaultAvatar($event)"
-              alt="Avatar"
-            >
+            <my-account-avatar [account]="accountBlock.blockedAccount"></my-account-avatar>
             <div>
               {{ accountBlock.blockedAccount.displayName }}
               <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span>
index d1ecc28342d58e28e9c3843334ef061a919b4c33..d360c3c51043559cb6c91be006fb50bc42799a67 100644 (file)
       <td>
         <a [href]="videoComment.account.localUrl" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
           <div class="chip two-lines">
-            <img
-              class="avatar"
-              [src]="videoComment.accountAvatarUrl"
-              alt=""
-            >
-            <div>
+            <my-account-avatar [account]="videoComment.account"></my-account-avatar>
+          <div>
               {{ videoComment.account.displayName }}
               <span>{{ videoComment.by }}</span>
             </div>
index 4a09fb392ed9bdc90b6b918526bd1332a2982e6d..e114f3425e800f724205cf38a5c245756d84984b 100644 (file)
       <td *ngIf="isSelected('username')">
         <a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]">
           <div class="chip two-lines">
-            <img
-              class="avatar"
-              [src]="user?.account?.avatar?.path"
-              (error)="switchToDefaultAvatar($event)"
-              alt="Avatar"
-            >
-            <div>
+            <my-account-avatar [account]="user?.account"></my-account-avatar>
+           <div>
               <span class="user-table-primary-text">{{ user.account.displayName }}</span>
               <span class="text-muted">{{ user.username }}</span>
             </div>
index 7875b74ad0f0c75599c3571e4df7b0e28b581da3..339e182067c4610febaaddc509fe3570e4167308 100644 (file)
@@ -163,10 +163,6 @@ export class UserListComponent extends RestTable implements OnInit {
     this.loadData()
   }
 
-  switchToDefaultAvatar ($event: Event) {
-    ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
-  }
-
   async unbanUsers (users: User[]) {
     const res = await this.confirmService.confirm($localize`Do you really want to unban ${users.length} users?`, $localize`Unban`)
     if (res === false) return
index 3df48d0aa7bcca99e262acdc573eb869e62a520d..050cd4b34fee2f3cbce265303c469b4436be2777 100644 (file)
@@ -23,6 +23,7 @@ import { MyAccountNotificationPreferencesComponent } from './my-account-settings
 import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component'
 import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component'
 import { MyAccountComponent } from './my-account.component'
+import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module'
 
 @NgModule({
   imports: [
@@ -39,6 +40,7 @@ import { MyAccountComponent } from './my-account.component'
     SharedGlobalIconModule,
     SharedAbuseListModule,
     SharedShareModal,
+    SharedAccountAvatarModule,
     SharedActorImageModule
   ],
 
index 5518cfd98731ed55316fde9220e5c46e051bbac3..a1d706f0bf35e2ecc61f4c776abf063795baac67 100644 (file)
@@ -26,6 +26,7 @@ import { MyVideoPlaylistUpdateComponent } from './my-video-playlists/my-video-pl
 import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component'
 import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component'
 import { MyVideosComponent } from './my-videos/my-videos.component'
+import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module'
 
 @NgModule({
   imports: [
@@ -45,7 +46,8 @@ import { MyVideosComponent } from './my-videos/my-videos.component'
     SharedGlobalIconModule,
     SharedAbuseListModule,
     SharedShareModal,
-    SharedVideoLiveModule
+    SharedVideoLiveModule,
+    SharedAccountAvatarModule
   ],
 
   declarations: [
index 6bf56298602794e2cd01d4773f712c98f6889c14..d0eff0521bae7b008f0e9a45ca66569d259b1c2a 100644 (file)
       <td>
         <a [href]="videoChangeOwnership.initiatorAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
           <div class="chip two-lines">
-            <img
-              class="avatar"
-              [src]="videoChangeOwnership.initiatorAccount.avatar?.path"
-              (error)="switchToDefaultAvatar($event)"
-              alt="Avatar"
-            >
+            <my-account-avatar [account]="videoChangeOwnership.initiatorAccount"></my-account-avatar>
             <div>
               {{ videoChangeOwnership.initiatorAccount.displayName }}
               <span class="text-muted">{{ videoChangeOwnership.initiatorAccount.nameWithHost }}</span>
index 78c3d91927b1dd64faf6f896cb9ae1cd9d3b1684..a938023b4e4ecf0cdeb9d81416d4eaee347f6a78 100644 (file)
@@ -43,10 +43,6 @@ export class MyOwnershipComponent extends RestTable implements OnInit {
     }
   }
 
-  switchToDefaultAvatar ($event: Event) {
-    ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
-  }
-
   openAcceptModal (videoChangeOwnership: VideoChangeOwnership) {
     this.myAccountAcceptOwnershipComponent.show(videoChangeOwnership)
   }
index b058a81856b38388f6c9cc7924ca7c403aec53c5..c0d8c7db4d6a849b1bccb3f333d323cbe3a20140 100644 (file)
         <div class="section-label" i18n>OWNER ACCOUNT</div>
 
         <div class="avatar-row">
-          <a [routerLink]="getAccountUrl()" title="View account" i18n-title>
-            <img class="account-avatar" [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" />
-          </a>
-
+          <my-account-avatar [account]="videoChannel.ownerAccount" size="120"></my-account-avatar>
+  
           <div class="actor-info">
             <h4>
               <a [routerLink]="getAccountUrl()" title="View account" i18n-title>{{ videoChannel.ownerAccount.displayName }}</a>
index 408f8622558699d8faa875874a6c10a787fd771a..2e387f40177c3da287531f32afccc247cba2255b 100644 (file)
@@ -10,6 +10,7 @@ import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-
 import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component'
 import { VideoChannelsRoutingModule } from './video-channels-routing.module'
 import { VideoChannelsComponent } from './video-channels.component'
+import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module'
 
 @NgModule({
   imports: [
@@ -21,7 +22,8 @@ import { VideoChannelsComponent } from './video-channels.component'
     SharedVideoMiniatureModule,
     SharedUserSubscriptionModule,
     SharedGlobalIconModule,
-    SharedSupportModal
+    SharedSupportModal,
+    SharedAccountAvatarModule
   ],
 
   declarations: [
index fdefed09a57fad931a588543e4f61d7b3de1613b..9304640246f748ed0c4682bf9a05e6f892041e0a 100644 (file)
@@ -1,6 +1,6 @@
 <form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
   <div class="avatar-and-textarea">
-    <img [src]="getAvatarUrl()" alt="Avatar" />
+    <my-account-avatar [account]="user?.account" size="25"></my-account-avatar>
 
     <div class="form-group">
       <textarea i18n-placeholder placeholder="Add comment..." myAutoResize
index d938e2e288943b23baeffeabc4e9dfa43c0ef7cc..e11b9be5af78e94d4137d25ae91b50680c3a4888 100644 (file)
@@ -9,9 +9,7 @@ form {
   display: flex;
   margin-bottom: 10px;
 
-  img {
-    @include avatar(25px);
-
+  my-account-avatar {
     vertical-align: top;
     margin-right: 10px;
   }
index f1f0dfeba139b7183bd4b48eaf109beb511e00ee..fce7e5edc6f47819a9c32a3c368ebf63a532849a 100644 (file)
@@ -143,11 +143,6 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges,
     return window.location.href
   }
 
-  getAvatarUrl () {
-    if (this.user) return this.user.accountAvatarUrl
-    return Account.GET_DEFAULT_AVATAR_URL()
-  }
-
   gotoLogin () {
     this.hideModals()
     this.router.navigate([ '/login' ])
index ba41b6f480b6df2f44a6d30b525eaee9a0230c8f..f13b12b25d06c94e9359a640e66cd8980eb90743 100644 (file)
@@ -1,14 +1,6 @@
 <div *ngIf="isCommentDisplayed()" class="root-comment">
   <div class="left">
-    <a *ngIf="!comment.isDeleted" [href]="comment.account.url" target="_blank" rel="noopener noreferrer">
-      <img
-        class="comment-avatar"
-        [src]="comment.accountAvatarUrl"
-        (error)="switchToDefaultAvatar($event)"
-        alt="Avatar"
-      />
-    </a>
-
+    <my-account-avatar *ngIf="!comment.isDeleted" [account]="comment.account"></my-account-avatar>
     <div class="vertical-border"></div>
   </div>
 
index f6ff376b9fcc5e52283b6f6924ee00e777c904a1..866293eea3d9aede5d5a1ef4828b53863c927336 100644 (file)
     width: 100%;
   }
 
-  .comment-avatar {
-    @include avatar(36px);
-  }
-
   .comment {
     flex-grow: 1;
     // Fix word-wrap with flex
index 5c5d72b2291f03854660475a92767f5e8da52284..dd3db0c6573e29b92c8cc49425d6ba554443f1db 100644 (file)
@@ -131,10 +131,6 @@ export class VideoCommentComponent implements OnInit, OnChanges {
     )
   }
 
-  switchToDefaultAvatar ($event: Event) {
-    ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
-  }
-
   isCommentDisplayed () {
     // Not deleted
     return !this.comment.isDeleted ||
index 5058f05ddebb72e7de282461a95eb6a98ed03936..545ecb1d5adbb5645232db307a513be3197056f1 100644 (file)
@@ -4,15 +4,11 @@
       <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" />
     </a>
 
-    <a [routerLink]="[ '/accounts', video.byAccount ]" [title]="accountLinkTitle">
-      <img [src]="video.accountAvatarUrl" i18n-alt alt="Account avatar" />
-    </a>
-  </ng-container>
+    <my-account-avatar [account]="video.account"></my-account-avatar>
+</ng-container>
 
   <ng-container *ngIf="!isChannelAvatarNull() && genericChannel">
-    <a [routerLink]="[ '/accounts', video.byAccount ]" [title]="accountLinkTitle">
-      <img [src]="video.accountAvatarUrl" i18n-alt alt="Account avatar" />
-    </a>
+    <my-account-avatar [account]="video.account"></my-account-avatar>
 
     <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle">
       <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" />
@@ -20,8 +16,6 @@
   </ng-container>
 
   <ng-container *ngIf="isChannelAvatarNull()">
-    <a [routerLink]="[ '/accounts', video.byAccount ]" [title]="accountLinkTitle">
-      <img [src]="video.accountAvatarUrl" i18n-alt alt="Account avatar" />
-    </a>
+    <my-account-avatar [account]="video.account"></my-account-avatar>
   </ng-container>
 </div>
index 29ad92f009058fd7f0a7abd5a14f81e0d51252e3..cf6afd852f78eb897ad94637def6f96202737dd5 100644 (file)
@@ -17,10 +17,11 @@ import { VideoCommentsComponent } from './comment/video-comments.component'
 import { PlayerStylesComponent } from './player-styles.component'
 import { RecommendationsModule } from './recommendations/recommendations.module'
 import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive'
-import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
 import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
 import { VideoWatchRoutingModule } from './video-watch-routing.module'
 import { VideoWatchComponent } from './video-watch.component'
+import { SharedAccountAvatarModule } from '../../shared/shared-account-avatar/shared-account-avatar.module'
+import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
 
 @NgModule({
   imports: [
@@ -37,7 +38,8 @@ import { VideoWatchComponent } from './video-watch.component'
     SharedVideoCommentModule,
     SharedShareModal,
     SharedVideoModule,
-    SharedSupportModal
+    SharedSupportModal,
+    SharedAccountAvatarModule
   ],
 
   declarations: [
@@ -47,6 +49,7 @@ import { VideoWatchComponent } from './video-watch.component'
     VideoCommentsComponent,
     VideoCommentAddComponent,
     VideoCommentComponent,
+    VideoAvatarChannelComponent,
 
     VideoAvatarChannelComponent,
 
index f790a684865295559747f4144913ee6c51ef1689..41c59cc86da5d9f01f7db5fd969a10a3b6df8e0b 100644 (file)
@@ -24,6 +24,7 @@ import { SharedGlobalIconModule } from './shared/shared-icons'
 import { SharedInstanceModule } from './shared/shared-instance'
 import { SharedMainModule } from './shared/shared-main'
 import { SharedUserInterfaceSettingsModule } from './shared/shared-user-settings'
+import { SharedAccountAvatarModule } from './shared/shared-account-avatar/shared-account-avatar.module'
 
 registerLocaleData(localeOc, 'oc')
 
@@ -59,6 +60,7 @@ registerLocaleData(localeOc, 'oc')
     SharedUserInterfaceSettingsModule,
     SharedGlobalIconModule,
     SharedInstanceModule,
+    SharedAccountAvatarModule,
 
     MetaModule.forRoot({
       provide: MetaLoader,
index 8aaaa238d8f03a8401bf830b4eafa16d1ad89b55..7d03e1c40f07167da7781e2130d64faa36283154 100644 (file)
@@ -111,12 +111,6 @@ export class User implements UserServerModel {
     }
   }
 
-  get accountAvatarUrl () {
-    if (!this.account) return ''
-
-    return this.account.avatarUrl
-  }
-
   hasRight (right: UserRight) {
     return hasUserRight(this.role, right)
   }
index 9aa397edd843b80e6499641ed4bca41bf206886b..a0777660f818d1b963310fa648f458549a6e71b6 100644 (file)
@@ -5,7 +5,7 @@
         <div>
           <div class="logged-in-more" ngbDropdown #dropdown="ngbDropdown" placement="bottom-left" [container]="dropdownContainer" (openChange)="onDropdownOpenChange($event)" autoClose="outside">
             <div ngbDropdownToggle>
-              <img [src]="user.accountAvatarUrl" alt="Avatar" />
+              <my-account-avatar [account]="user.account" size="34"></my-account-avatar>
               <div class="logged-in-info">
                 <div class="logged-in-display-name">{{ user.account?.displayName }}</div>
 
index aa247d268fc4e5e78b0304fd1026e403f3c6877f..729dbc6ee529a1a7195550ba43aaa49921a9f088 100644 (file)
@@ -176,9 +176,7 @@ menu {
     border-radius: $main-radius;
   }
 
-  img {
-    @include avatar(34px);
-
+  my-account-avatar {
     margin-right: 10px;
   }
 }
index fb8366f4ce74581ccca3d0a904096b1c22bfd3c0..658d425374e17764ddbeda929eef9c0e1cbf2248 100644 (file)
         <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }"
           class="chip"
         >
-          <img
-            class="avatar"
-            [src]="abuse.reporterAccount.avatar?.path"
-            (error)="switchToDefaultAvatar($event)"
-            alt="Avatar"
-          >
+          <my-account-avatar [account]="abuse.reporterAccount"></my-account-avatar>
           <div>
             <span class="text-muted">{{ abuse.reporterAccount.nameWithHost }}</span>
           </div>
         <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }"
           class="chip"
         >
-          <img
-            class="avatar"
-            [src]="abuse.flaggedAccount?.avatar?.path"
-            (error)="switchToDefaultAvatar($event)"
-            alt="Avatar"
-          >
+          <my-account-avatar [account]="abuse.flaggedAccount"></my-account-avatar>
           <div>
             <span class="text-muted">{{ abuse.flaggedAccount ? abuse.flaggedAccount.nameWithHost : '' }}</span>
           </div>
index 31cf3389d41b6aa469905aa191926f89f906ad27..e8ce7e678338a5c5182491cba0c4d5c6bf09ba11 100644 (file)
@@ -45,8 +45,4 @@ export class AbuseDetailsComponent {
       label: this.predefinedReasonsTranslations[r]
     }))
   }
-
-  switchToDefaultAvatar ($event: Event) {
-    ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
-  }
 }
index 8428032bf58e56df4d3d87ccda1278e8f5c1fc2f..29b51f09c20a4ba130d2fd0fbbd2326772f55e8d 100644 (file)
       <td *ngIf="isAdminView()">
         <a *ngIf="abuse.reporterAccount" [href]="abuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
           <div class="chip two-lines">
-            <img
-              class="avatar"
-              [src]="abuse.reporterAccount.avatar?.path"
-              (error)="switchToDefaultAvatar($event)"
-              alt="Avatar"
-            >
+            <my-account-avatar [account]="abuse.reporterAccount"></my-account-avatar>
             <div>
               {{ abuse.reporterAccount.displayName }}
               <span>{{ abuse.reporterAccount.nameWithHost }}</span>
index eeb9f128b623e744a1212cba87900f4e0116994f..8b5771237e30605109a1dd1ab21dbea76b6edbba 100644 (file)
@@ -122,10 +122,6 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
     )
   }
 
-  switchToDefaultAvatar ($event: Event) {
-    ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
-  }
-
   async removeAbuse (abuse: AdminAbuse) {
     const res = await this.confirmService.confirm($localize`Do you really want to delete this abuse report?`, $localize`Delete`)
     if (res === false) return
index 663cd902b2457a04e72c71e6286fb2b0d66fd17e..19b6d456d145ea4007c69547e568918a8c5aa51c 100644 (file)
@@ -10,6 +10,7 @@ import { AbuseDetailsComponent } from './abuse-details.component'
 import { AbuseListTableComponent } from './abuse-list-table.component'
 import { AbuseMessageModalComponent } from './abuse-message-modal.component'
 import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
+import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module'
 
 @NgModule({
   imports: [
@@ -19,7 +20,8 @@ import { ModerationCommentModalComponent } from './moderation-comment-modal.comp
     SharedFormModule,
     SharedModerationModule,
     SharedGlobalIconModule,
-    SharedVideoCommentModule
+    SharedVideoCommentModule,
+    SharedAccountAvatarModule
   ],
 
   declarations: [
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.html b/client/src/app/shared/shared-account-avatar/account-avatar.component.html
new file mode 100644 (file)
index 0000000..6bec0b5
--- /dev/null
@@ -0,0 +1,8 @@
+<a *ngIf="account" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title">
+  <img
+    [class]="class"
+    [src]="avatarUrl"
+    i18n-alt
+    alt="Account avatar"
+  />
+</a>
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.scss b/client/src/app/shared/shared-account-avatar/account-avatar.component.scss
new file mode 100644 (file)
index 0000000..bb941d7
--- /dev/null
@@ -0,0 +1,22 @@
+@import '_variables';
+@import '_mixins';
+
+.avatar-25 {
+  @include avatar(25px);
+}
+
+.avatar-34 {
+  @include avatar(34px);
+}
+
+.avatar-36 {
+  @include avatar(36px);
+}
+
+.avatar-40 {
+  @include avatar(40px);
+}
+
+.avatar-120 {
+  @include avatar(120px);
+}
\ No newline at end of file
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.ts b/client/src/app/shared/shared-account-avatar/account-avatar.component.ts
new file mode 100644 (file)
index 0000000..d5533d4
--- /dev/null
@@ -0,0 +1,38 @@
+import { Component, Input } from '@angular/core'
+import { Account as AccountInterface } from '@shared/models'
+import { Account } from '../shared-main/account/account.model'
+
+@Component({
+  selector: 'my-account-avatar',
+  styleUrls: [ './account-avatar.component.scss' ],
+  templateUrl: './account-avatar.component.html'
+})
+export class AccountAvatarComponent {
+  _href: string
+  _title: string
+
+  @Input() account: { name: string, avatar?: { url?: string }, url: string }
+  @Input() size = '36'
+  @Input() set href (value) {
+    this._href = value
+  }
+  @Input() set title (value) {
+    this._title = value
+  }
+
+  get href () {
+    return this._href || this.account?.url
+  }
+
+  get title () {
+    return this._title || $localize`${this.account.name} (account page)`
+  }
+
+  get class () {
+    return `avatar avatar-${this.size}`
+  }
+
+  get avatarUrl () {
+    return this.account?.avatar ? this.account.avatar.url : Account.GET_DEFAULT_AVATAR_URL()
+  }
+}
diff --git a/client/src/app/shared/shared-account-avatar/index.ts b/client/src/app/shared/shared-account-avatar/index.ts
new file mode 100644 (file)
index 0000000..40c742b
--- /dev/null
@@ -0,0 +1,2 @@
+export * from './account-avatar.component'
+export * from './shared-account-avatar.module'
\ No newline at end of file
diff --git a/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts b/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts
new file mode 100644 (file)
index 0000000..17b2758
--- /dev/null
@@ -0,0 +1,23 @@
+
+import { NgModule } from '@angular/core'
+import { SharedGlobalIconModule } from '../shared-icons'
+import { SharedMainModule } from '../shared-main/shared-main.module'
+import { AccountAvatarComponent } from './account-avatar.component'
+
+@NgModule({
+  imports: [
+    SharedMainModule,
+    SharedGlobalIconModule
+  ],
+
+  declarations: [
+    AccountAvatarComponent
+  ],
+
+  exports: [
+    AccountAvatarComponent
+  ],
+
+  providers: [ ]
+})
+export class SharedAccountAvatarModule { }
index 1c2c4a575199b7d358212c9f7312d9165ec7db73..14c507295abe9c45a900f574c8ae82fa6b4dd31f 100644 (file)
@@ -20,7 +20,6 @@ export class Video implements VideoServerModel {
   byVideoChannel: string
   byAccount: string
 
-  accountAvatarUrl: string
   videoChannelAvatarUrl: string
 
   createdAt: Date
@@ -144,7 +143,6 @@ export class Video implements VideoServerModel {
 
     this.byAccount = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host)
     this.byVideoChannel = Actor.CREATE_BY_STRING(hash.channel.name, hash.channel.host)
-    this.accountAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.account)
     this.videoChannelAvatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this.channel)
 
     this.category.label = peertubeTranslate(this.category.label, translations)
index fb6f69cd52ae45049643941964c600ee734732cd..3f2f5555945d7f6c6a3187ec9e617c22d232390c 100644 (file)
       <td>
         <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
           <div class="chip two-lines">
-            <img
-              class="avatar"
-              [src]="accountBlock.blockedAccount.avatar?.path"
-              (error)="switchToDefaultAvatar($event)"
-              alt="Avatar"
-            >
+            <my-account-avatar [account]="accountBlock.blockedAccount"></my-account-avatar>
             <div>
               {{ accountBlock.blockedAccount.displayName }}
               <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span>
index 3de9587b8076976c6161e185f47243af3e1f2b35..1bce65bf02722b46acd97908d78ef90f7de93161 100644 (file)
@@ -30,10 +30,6 @@ export class GenericAccountBlocklistComponent extends RestTable implements OnIni
     this.initialize()
   }
 
-  switchToDefaultAvatar ($event: Event) {
-    ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
-  }
-
   unblockAccount (accountBlock: AccountBlock) {
     const blockedAccount = accountBlock.blockedAccount
     const operation = this.mode === BlocklistComponentType.Account
index b1b98f8d06b916237628cf4156c892d50d8751d6..c7e201792c419fc461857b09556561eedefc4ff3 100644 (file)
@@ -13,13 +13,15 @@ import { UserBanModalComponent } from './user-ban-modal.component'
 import { UserModerationDropdownComponent } from './user-moderation-dropdown.component'
 import { VideoBlockComponent } from './video-block.component'
 import { VideoBlockService } from './video-block.service'
+import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module'
 
 @NgModule({
   imports: [
     SharedMainModule,
     SharedFormModule,
     SharedGlobalIconModule,
-    SharedVideoCommentModule
+    SharedVideoCommentModule,
+    SharedAccountAvatarModule
   ],
 
   declarations: [
index bf718ae082684840ae9ab6d19153dcdb781f7f6d..9a4e3954e5b2bdcf6bc6ddd6c9e2d7d3a9a01e5b 100644 (file)
@@ -17,7 +17,6 @@ export class VideoComment implements VideoCommentServerModel {
   totalRepliesFromVideoAuthor: number
   totalReplies: number
   by: string
-  accountAvatarUrl: string
 
   isLocal: boolean
 
@@ -38,7 +37,6 @@ export class VideoComment implements VideoCommentServerModel {
 
     if (this.account) {
       this.by = Actor.CREATE_BY_STRING(this.account.name, this.account.host)
-      this.accountAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.account)
 
       const absoluteAPIUrl = getAbsoluteAPIUrl()
       const thisHost = new URL(absoluteAPIUrl).host
@@ -70,7 +68,6 @@ export class VideoCommentAdmin implements VideoCommentAdminServerModel {
   }
 
   by: string
-  accountAvatarUrl: string
 
   constructor (hash: VideoCommentAdminServerModel, textHtml: string) {
     this.id = hash.id
@@ -97,7 +94,6 @@ export class VideoCommentAdmin implements VideoCommentAdminServerModel {
 
     if (this.account) {
       this.by = Actor.CREATE_BY_STRING(this.account.name, this.account.host)
-      this.accountAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.account)
 
       this.account.localUrl = '/accounts/' + this.by
     }
index 7a786885318ef68c9a81811dc726aa2510ad0421..32cfdfd685125245c04ff9b13fad7d6d914663cd 100644 (file)
@@ -13,6 +13,7 @@ import { VideoDownloadComponent } from './video-download.component'
 import { VideoMiniatureComponent } from './video-miniature.component'
 import { VideosSelectionComponent } from './videos-selection.component'
 import { VideoListHeaderComponent } from './video-list-header.component'
+import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module'
 
 @NgModule({
   imports: [
@@ -23,7 +24,8 @@ import { VideoListHeaderComponent } from './video-list-header.component'
     SharedThumbnailModule,
     SharedGlobalIconModule,
     SharedVideoLiveModule,
-    SharedVideoModule
+    SharedVideoModule,
+    SharedAccountAvatarModule
   ],
 
   declarations: [
index bac8bcc2db6f6e16bdfcda57741a2866226b3658..4e61c27b33156c8ebc86c464bf45b4d2d31468f8 100644 (file)
   <div class="video-bottom">
     <div class="video-miniature-information">
       <div class="d-flex video-miniature-meta">
-        <a *ngIf="displayOptions.avatar" class="avatar" [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle">
-          <img [src]="getAvatarUrl()" alt="" [ngClass]="{ channel: displayOwnerVideoChannel() }" />
+        <a *ngIf="displayOptions.avatar && displayOwnerVideoChannel()" class="avatar" [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle">
+          <img [src]="getAvatarUrl()" alt="" class="channel" />
         </a>
+        <my-account-avatar *ngIf="displayOptions.avatar && displayOwnerAccount()" [account]="video.account" size="40" [href]="'/video-channels/' + video.byVideoChannel" [title]="channelLinkTitle"></my-account-avatar>
 
         <div class="w-100 d-flex flex-column">
           <a *ngIf="!videoHref" tabindex="-1" class="video-miniature-name"
index 62195191910ace7f65a0326e8e2e8d9da26b73ba..bf52b870c6b05cb3233686ecc2bd1db37f1ed95e 100644 (file)
@@ -12,13 +12,10 @@ $more-button-width: 40px;
   width: calc(100% - #{$more-button-width});
 }
 
+my-account-avatar,
 .avatar {
   margin: 10px 10px 0 0;
 
-  img:not(.channel) {
-    @include avatar(40px);
-  }
-
   img.channel {
     @include channel-avatar(40px);
   }
index 48da92d6b5eab70174c63585dd2126d67b98263d..8d66aaee2c9a42bc495dd3f83e103896b6ce149e 100644 (file)
@@ -182,7 +182,7 @@ export class VideoMiniatureComponent implements OnInit {
 
   getAvatarUrl () {
     if (this.displayOwnerAccount()) {
-      return this.video.accountAvatarUrl
+      return this.video.account.avatar?.url
     }
 
     return this.video.videoChannelAvatarUrl
index 8d82a042ce58a9e1644cf82a31cf5796d1d58c5f..a4798ce1d68ff58ee9111b2df9f2d847c20074e2 100644 (file)
     @include channel-avatar(120px);
   }
 
-  .account-avatar {
-    @include avatar(120px);
-  }
-
   > div {
     margin-left: $img-margin;
     min-width: 1px;