Skip to content

Commit

Permalink
Add ability to set avatar to instance
Browse files Browse the repository at this point in the history
  • Loading branch information
Chocobozzz committed Feb 23, 2024
1 parent db06d13 commit bb7cb0d
Show file tree
Hide file tree
Showing 29 changed files with 687 additions and 342 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,25 @@ <h2 i18n class="section-left-column-title">INSTANCE</h2>
</div>

<div class="col-12 col-lg-8 col-xl-9">
<div class="form-group">
<label i18n for="avatarfile">Square icon</label>

<div class="label-small-info">
<p i18n class="mb-0">Square icon can be used on your custom homepage.</p>
</div>

<my-actor-avatar-edit
class="d-block mb-4"
actorType="account" previewImage="false" [username]="instanceName" displayUsername="false"
[avatars]="instanceAvatars" (avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()"
></my-actor-avatar-edit>
</div>

<div class="form-group">
<label i18n for="bannerfile">Banner</label>

<div class="label-small-info">
<p i18n class="mb-0">Banner is displayed in the about, login and registration pages.</p>
<p i18n class="mb-0">Banner is displayed in the about, login and registration pages and be used on your custom homepage.</p>
<p i18n>It can also be displayed on external websites to promote your instance, such as <a target="_blank" href="https://joinpeertube.org/instances">JoinPeerTube.org</a>.</p>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { SelectOptionsItem } from 'src/types/select-options-item.model'
import { Component, Input, OnInit } from '@angular/core'
import { FormGroup } from '@angular/forms'
import { CustomMarkupService } from '@app/shared/shared-custom-markup'
import { Notifier } from '@app/core'
import { Notifier, ServerService } from '@app/core'
import { HttpErrorResponse } from '@angular/common/http'
import { genericUploadErrorHandler } from '@app/helpers'
import { InstanceService } from '@app/shared/shared-instance'
import { ActorImage, HTMLServerConfig } from '@peertube/peertube-models'

@Component({
selector: 'my-edit-instance-information',
Expand All @@ -20,17 +21,27 @@ export class EditInstanceInformationComponent implements OnInit {
@Input() categoryItems: SelectOptionsItem[] = []

instanceBannerUrl: string
instanceAvatars: ActorImage[] = []

private serverConfig: HTMLServerConfig

constructor (
private customMarkup: CustomMarkupService,
private notifier: Notifier,
private instanceService: InstanceService
private instanceService: InstanceService,
private server: ServerService
) {

}

get instanceName () {
return this.server.getHTMLConfig().instance.name
}

ngOnInit () {
this.resetBannerUrl()
this.serverConfig = this.server.getHTMLConfig()

this.updateActorImages()
}

getCustomMarkdownRenderer () {
Expand All @@ -39,15 +50,15 @@ export class EditInstanceInformationComponent implements OnInit {

onBannerChange (formData: FormData) {
this.instanceService.updateInstanceBanner(formData)
.subscribe({
next: () => {
this.notifier.success($localize`Banner changed.`)
.subscribe({
next: () => {
this.notifier.success($localize`Banner changed.`)

this.resetBannerUrl()
},
this.resetActorImages()
},

error: (err: HttpErrorResponse) => genericUploadErrorHandler({ err, name: $localize`banner`, notifier: this.notifier })
})
error: (err: HttpErrorResponse) => genericUploadErrorHandler({ err, name: $localize`banner`, notifier: this.notifier })
})
}

onBannerDelete () {
Expand All @@ -56,17 +67,51 @@ export class EditInstanceInformationComponent implements OnInit {
next: () => {
this.notifier.success($localize`Banner deleted.`)

this.resetBannerUrl()
this.resetActorImages()
},

error: err => this.notifier.error(err.message)
})
}

private resetBannerUrl () {
this.instanceService.getInstanceBannerUrl()
.subscribe(instanceBannerUrl => {
this.instanceBannerUrl = instanceBannerUrl
onAvatarChange (formData: FormData) {
this.instanceService.updateInstanceAvatar(formData)
.subscribe({
next: () => {
this.notifier.success($localize`Avatar changed.`)

this.resetActorImages()
},

error: (err: HttpErrorResponse) => genericUploadErrorHandler({ err, name: $localize`avatar`, notifier: this.notifier })
})
}

onAvatarDelete () {
this.instanceService.deleteInstanceAvatar()
.subscribe({
next: () => {
this.notifier.success($localize`Avatar deleted.`)

this.resetActorImages()
},

error: err => this.notifier.error(err.message)
})
}

private updateActorImages () {
this.instanceBannerUrl = this.serverConfig.instance.banners?.[0]?.path
this.instanceAvatars = this.serverConfig.instance.avatars
}

private resetActorImages () {
this.server.resetConfig()
.subscribe(config => {
this.serverConfig = config

this.updateActorImages()
})
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,18 @@
<div class="row mt-4"> <!-- user grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div class="anchor" id="user"></div> <!-- user anchor -->
<div *ngIf="isCreation()" class="section-left-column-title" i18n>NEW USER</div>
<div *ngIf="!isCreation() && user" class="section-left-column-title">
<my-actor-avatar-edit [actor]="user.account" [editable]="false" [displaySubscribers]="false" [displayUsername]="false"></my-actor-avatar-edit>
</div>

@if (isCreation()) {
<div class="section-left-column-title" i18n>NEW USER</div>
} @else if (user) {
<div class="section-left-column-title">
<my-actor-avatar-edit
actorType="account" [displayName]="user.account.displayName" [avatars]="user.account.avatars"
editable="false" [username]="user.username" displayUsername="false"
></my-actor-avatar-edit>
</div>
}

</div>

<div class="col-12 col-lg-8 col-xl-9">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
></my-actor-banner-edit>

<my-actor-avatar-edit
*ngIf="videoChannel" [previewImage]="isCreation()" class="d-block mb-4"
[actor]="videoChannel" (avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()"
[displayUsername]="!isCreation()" [displaySubscribers]="!isCreation()"
*ngIf="videoChannel" class="d-block mb-4" actorType="channel"
[displayName]="videoChannel.displayName" [previewImage]="isCreation()" [avatars]="videoChannel.avatars"
[username]="!isCreation() && videoChannel.displayName" [subscribers]="!isCreation() && videoChannel.followersCount"
(avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()"
></my-actor-avatar-edit>

<div class="form-group" *ngIf="isCreation()">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ <h1 class="visually-hidden" i18n>Settings</h1>
<div class="col-12 col-lg-4 col-xl-3"></div>

<div class="col-12 col-lg-8 col-xl-9">
<my-actor-avatar-edit [actor]="user.account" (avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()"></my-actor-avatar-edit>
<my-actor-avatar-edit
actorType="account" [avatars]="user.account.avatars"
[displayName]="user.account.displayName" [username]="user.username" [subscribers]="user.account.followersCount"
(avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()"
></my-actor-avatar-edit>
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="actor" *ngIf="actor">
<div class="position-relative me-3">
<my-actor-avatar [actor]="actor" [actorType]="getActorType()" [previewImage]="preview" size="100"></my-actor-avatar>
<my-actor-avatar [actor]="actor" [actorType]="actorType" [previewImage]="preview" size="100"></my-actor-avatar>

<div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button button-focus-within" [ngbTooltip]="avatarFormat" placement="right" container="body">
<my-global-icon iconName="upload"></my-global-icon>
Expand Down Expand Up @@ -31,8 +31,8 @@
</div>

<div class="actor-info">
<div class="actor-info-display-name">{{ actor.displayName }}</div>
<div *ngIf="displayUsername" class="actor-info-username">{{ actor.name }}</div>
<div *ngIf="displaySubscribers" i18n class="actor-info-followers">{{ actor.followersCount }} subscribers</div>
<div *ngIf="displayName" class="actor-info-display-name">{{ displayName }}</div>
<div *ngIf="displayUsername && username" class="actor-info-username">{{ username }}</div>
<div *ngIf="subscribers" i18n class="actor-info-followers">{{ subscribers }} subscribers</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild, booleanAttribute } from '@angular/core'
import { Notifier, ServerService } from '@app/core'
import { Account, VideoChannel } from '@app/shared/shared-main'
import { getBytes } from '@root-helpers/bytes'
import { imageToDataURL } from '@root-helpers/images'
import { ActorAvatarInput } from '../shared-actor-image/actor-avatar.component'
import { ActorImage } from '@peertube/peertube-models'

@Component({
selector: 'my-actor-avatar-edit',
Expand All @@ -12,14 +13,19 @@ import { imageToDataURL } from '@root-helpers/images'
'./actor-avatar-edit.component.scss'
]
})
export class ActorAvatarEditComponent implements OnInit {
export class ActorAvatarEditComponent implements OnInit, OnChanges {
@ViewChild('avatarfileInput') avatarfileInput: ElementRef<HTMLInputElement>

@Input() actor: VideoChannel | Account
@Input() editable = true
@Input() displaySubscribers = true
@Input() displayUsername = true
@Input() previewImage = false
@Input({ required: true }) actorType: 'channel' | 'account'
@Input({ required: true }) avatars: ActorImage[]
@Input({ required: true }) username: string

@Input() displayName: string
@Input() subscribers: number

@Input({ transform: booleanAttribute }) displayUsername = true
@Input({ transform: booleanAttribute }) editable = true
@Input({ transform: booleanAttribute }) previewImage = false

@Output() avatarChange = new EventEmitter<FormData>()
@Output() avatarDelete = new EventEmitter<void>()
Expand All @@ -30,6 +36,8 @@ export class ActorAvatarEditComponent implements OnInit {

preview: string

actor: ActorAvatarInput

constructor (
private serverService: ServerService,
private notifier: Notifier
Expand All @@ -41,8 +49,14 @@ export class ActorAvatarEditComponent implements OnInit {
this.maxAvatarSize = config.avatar.file.size.max
this.avatarExtensions = config.avatar.file.extensions.join(', ')

this.avatarFormat = `${$localize`max size`}: 192*192px, ` +
`${getBytes(this.maxAvatarSize)} ${$localize`extensions`}: ${this.avatarExtensions}`
this.avatarFormat = $localize`max size: 192*192px, ${getBytes(this.maxAvatarSize)} extensions: ${this.avatarExtensions}`
}

ngOnChanges () {
this.actor = {
avatars: this.avatars,
name: this.username
}
}

onAvatarChange (input: HTMLInputElement) {
Expand All @@ -69,12 +83,6 @@ export class ActorAvatarEditComponent implements OnInit {
}

hasAvatar () {
return !!this.preview || this.actor.avatars.length !== 0
}

getActorType () {
if ((this.actor as VideoChannel).ownerAccount) return 'channel'

return 'account'
return !!this.preview || this.avatars.length !== 0
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<ng-template #img>
<img *ngIf="displayImage()" [class]="classes" [src]="previewImage || avatarUrl || defaultAvatarUrl" alt="" />
<img #avatarEl *ngIf="displayImage()" [ngClass]="classes" [src]="previewImage || avatarUrl || defaultAvatarUrl" alt="" />

<div *ngIf="displayActorInitial()" [ngClass]="classes">
<div #avatarEl *ngIf="displayActorInitial()" [ngClass]="classes">
<span>{{ getActorInitial() }}</span>
</div>

<div *ngIf="displayPlaceholder()" [ngClass]="classes"></div>
<div #avatarEl *ngIf="displayPlaceholder()" [ngClass]="classes"></div>
</ng-template>

<a *ngIf="actor && href" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
@use '_mixins' as *;

.avatar {
--avatarSize: 100%;
--initialFontSize: 22px;

// Defined in component
width: var(--avatarSize);
height: var(--avatarSize);
min-width: var(--avatarSize);
Expand All @@ -20,26 +18,6 @@
}
}

$sizes: '18', '25', '28', '32', '34', '35', '36', '40', '48', '75', '80', '100', '120';

@each $size in $sizes {
.avatar-#{$size} {
--avatarSize: #{$size}px;
}
}

.avatar-18 {
--initialFontSize: 13px;
}

.avatar-100 {
--initialFontSize: 40px;
}

.avatar-120 {
--initialFontSize: 46px;
}

a:hover {
text-decoration: none;
}
Expand Down
Loading

0 comments on commit bb7cb0d

Please sign in to comment.