<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>
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: [
SharedUserSubscriptionModule,
SharedModerationModule,
SharedVideoMiniatureModule,
- SharedGlobalIconModule
+ SharedGlobalIconModule,
+ SharedAccountAvatarModule
],
declarations: [
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: [
SharedGlobalIconModule,
SharedAbuseListModule,
SharedVideoCommentModule,
+ SharedAccountAvatarModule,
SharedActorImageModule,
TableModule,
<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>
<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>
<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>
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
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: [
SharedGlobalIconModule,
SharedAbuseListModule,
SharedShareModal,
+ SharedAccountAvatarModule,
SharedActorImageModule
],
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: [
SharedGlobalIconModule,
SharedAbuseListModule,
SharedShareModal,
- SharedVideoLiveModule
+ SharedVideoLiveModule,
+ SharedAccountAvatarModule
],
declarations: [
<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>
}
}
- switchToDefaultAvatar ($event: Event) {
- ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
- }
-
openAcceptModal (videoChangeOwnership: VideoChangeOwnership) {
this.myAccountAcceptOwnershipComponent.show(videoChangeOwnership)
}
<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>
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: [
SharedVideoMiniatureModule,
SharedUserSubscriptionModule,
SharedGlobalIconModule,
- SharedSupportModal
+ SharedSupportModal,
+ SharedAccountAvatarModule
],
declarations: [
<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
display: flex;
margin-bottom: 10px;
- img {
- @include avatar(25px);
-
+ my-account-avatar {
vertical-align: top;
margin-right: 10px;
}
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' ])
<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>
width: 100%;
}
- .comment-avatar {
- @include avatar(36px);
- }
-
.comment {
flex-grow: 1;
// Fix word-wrap with flex
)
}
- switchToDefaultAvatar ($event: Event) {
- ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
- }
-
isCommentDisplayed () {
// Not deleted
return !this.comment.isDeleted ||
<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" />
</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>
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: [
SharedVideoCommentModule,
SharedShareModal,
SharedVideoModule,
- SharedSupportModal
+ SharedSupportModal,
+ SharedAccountAvatarModule
],
declarations: [
VideoCommentsComponent,
VideoCommentAddComponent,
VideoCommentComponent,
+ VideoAvatarChannelComponent,
VideoAvatarChannelComponent,
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')
SharedUserInterfaceSettingsModule,
SharedGlobalIconModule,
SharedInstanceModule,
+ SharedAccountAvatarModule,
MetaModule.forRoot({
provide: MetaLoader,
}
}
- get accountAvatarUrl () {
- if (!this.account) return ''
-
- return this.account.avatarUrl
- }
-
hasRight (right: UserRight) {
return hasUserRight(this.role, right)
}
<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>
border-radius: $main-radius;
}
- img {
- @include avatar(34px);
-
+ my-account-avatar {
margin-right: 10px;
}
}
<a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reporter:"' + abuse.reporterAccount.displayName + '"' }"
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:"' +abuse.flaggedAccount.displayName + '"' }"
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>
label: this.predefinedReasonsTranslations[r]
}))
}
-
- switchToDefaultAvatar ($event: Event) {
- ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
- }
}
<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>
)
}
- 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
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: [
SharedFormModule,
SharedModerationModule,
SharedGlobalIconModule,
- SharedVideoCommentModule
+ SharedVideoCommentModule,
+ SharedAccountAvatarModule
],
declarations: [
--- /dev/null
+<a *ngIf="account" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title">
+ <img
+ [class]="class"
+ [src]="avatarUrl"
+ i18n-alt
+ alt="Account avatar"
+ />
+</a>
--- /dev/null
+@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
--- /dev/null
+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()
+ }
+}
--- /dev/null
+export * from './account-avatar.component'
+export * from './shared-account-avatar.module'
\ No newline at end of file
--- /dev/null
+
+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 { }
byVideoChannel: string
byAccount: string
- accountAvatarUrl: string
videoChannelAvatarUrl: string
createdAt: Date
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)
<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>
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
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: [
totalRepliesFromVideoAuthor: number
totalReplies: number
by: string
- accountAvatarUrl: string
isLocal: boolean
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
}
by: string
- accountAvatarUrl: string
constructor (hash: VideoCommentAdminServerModel, textHtml: string) {
this.id = hash.id
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
}
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: [
SharedThumbnailModule,
SharedGlobalIconModule,
SharedVideoLiveModule,
- SharedVideoModule
+ SharedVideoModule,
+ SharedAccountAvatarModule
],
declarations: [
<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"
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);
}
getAvatarUrl () {
if (this.displayOwnerAccount()) {
- return this.video.accountAvatarUrl
+ return this.video.account.avatar?.url
}
return this.video.videoChannelAvatarUrl
@include channel-avatar(120px);
}
- .account-avatar {
- @include avatar(120px);
- }
-
> div {
margin-left: $img-margin;
min-width: 1px;