From 67ed6552b831df66713bac9e672738796128d33f Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Jun 2020 14:10:17 +0200 Subject: Reorganize client shared modules --- .../shared/shared-main/account/account.model.ts | 30 +++++++++ .../shared/shared-main/account/account.service.ts | 29 +++++++++ .../account/actor-avatar-info.component.html | 24 ++++++++ .../account/actor-avatar-info.component.scss | 71 ++++++++++++++++++++++ .../account/actor-avatar-info.component.ts | 64 +++++++++++++++++++ .../app/shared/shared-main/account/actor.model.ts | 65 ++++++++++++++++++++ .../shared-main/account/avatar.component.html | 8 +++ .../shared-main/account/avatar.component.scss | 40 ++++++++++++ .../shared/shared-main/account/avatar.component.ts | 31 ++++++++++ client/src/app/shared/shared-main/account/index.ts | 5 ++ 10 files changed, 367 insertions(+) create mode 100644 client/src/app/shared/shared-main/account/account.model.ts create mode 100644 client/src/app/shared/shared-main/account/account.service.ts create mode 100644 client/src/app/shared/shared-main/account/actor-avatar-info.component.html create mode 100644 client/src/app/shared/shared-main/account/actor-avatar-info.component.scss create mode 100644 client/src/app/shared/shared-main/account/actor-avatar-info.component.ts create mode 100644 client/src/app/shared/shared-main/account/actor.model.ts create mode 100644 client/src/app/shared/shared-main/account/avatar.component.html create mode 100644 client/src/app/shared/shared-main/account/avatar.component.scss create mode 100644 client/src/app/shared/shared-main/account/avatar.component.ts create mode 100644 client/src/app/shared/shared-main/account/index.ts (limited to 'client/src/app/shared/shared-main/account') diff --git a/client/src/app/shared/shared-main/account/account.model.ts b/client/src/app/shared/shared-main/account/account.model.ts new file mode 100644 index 000000000..6df2e9d10 --- /dev/null +++ b/client/src/app/shared/shared-main/account/account.model.ts @@ -0,0 +1,30 @@ +import { Account as ServerAccount } from '@shared/models/actors/account.model' +import { Actor } from './actor.model' + +export class Account extends Actor implements ServerAccount { + displayName: string + description: string + nameWithHost: string + nameWithHostForced: string + mutedByUser: boolean + mutedByInstance: boolean + mutedServerByUser: boolean + mutedServerByInstance: boolean + + userId?: number + + constructor (hash: ServerAccount) { + super(hash) + + this.displayName = hash.displayName + this.description = hash.description + this.userId = hash.userId + this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host) + this.nameWithHostForced = Actor.CREATE_BY_STRING(this.name, this.host, true) + + this.mutedByUser = false + this.mutedByInstance = false + this.mutedServerByUser = false + this.mutedServerByInstance = false + } +} diff --git a/client/src/app/shared/shared-main/account/account.service.ts b/client/src/app/shared/shared-main/account/account.service.ts new file mode 100644 index 000000000..8f4abf070 --- /dev/null +++ b/client/src/app/shared/shared-main/account/account.service.ts @@ -0,0 +1,29 @@ +import { Observable, ReplaySubject } from 'rxjs' +import { catchError, map, tap } from 'rxjs/operators' +import { HttpClient } from '@angular/common/http' +import { Injectable } from '@angular/core' +import { RestExtractor } from '@app/core' +import { Account as ServerAccount } from '@shared/models' +import { environment } from '../../../../environments/environment' +import { Account } from './account.model' + +@Injectable() +export class AccountService { + static BASE_ACCOUNT_URL = environment.apiUrl + '/api/v1/accounts/' + + accountLoaded = new ReplaySubject(1) + + constructor ( + private authHttp: HttpClient, + private restExtractor: RestExtractor + ) {} + + getAccount (id: number | string): Observable { + return this.authHttp.get(AccountService.BASE_ACCOUNT_URL + id) + .pipe( + map(accountHash => new Account(accountHash)), + tap(account => this.accountLoaded.next(account)), + catchError(res => this.restExtractor.handleError(res)) + ) + } +} diff --git a/client/src/app/shared/shared-main/account/actor-avatar-info.component.html b/client/src/app/shared/shared-main/account/actor-avatar-info.component.html new file mode 100644 index 000000000..d01b9ac7f --- /dev/null +++ b/client/src/app/shared/shared-main/account/actor-avatar-info.component.html @@ -0,0 +1,24 @@ + +
+
+ Avatar + +
+
+ + + +
+
+
+ + +
+
+
{{ actor.displayName }}
+
{{ actor.name }}
+
+
{{ actor.followersCount }} subscribers
+
+
+
\ No newline at end of file diff --git a/client/src/app/shared/shared-main/account/actor-avatar-info.component.scss b/client/src/app/shared/shared-main/account/actor-avatar-info.component.scss new file mode 100644 index 000000000..5a66ecfd2 --- /dev/null +++ b/client/src/app/shared/shared-main/account/actor-avatar-info.component.scss @@ -0,0 +1,71 @@ +@import '_variables'; +@import '_mixins'; + +.actor { + display: flex; + + img { + @include avatar(100px); + + margin-right: 15px; + } + + .actor-img-edit-container { + position: relative; + width: 0; + + .actor-img-edit-button { + @include peertube-button-file(21px); + @include button-with-icon(19px); + + margin-top: 10px; + margin-bottom: 5px; + border-radius: 50%; + top: 55px; + right: 45px; + cursor: pointer; + + input { + width: 30px; + height: 30px; + } + + my-global-icon { + right: 7px; + } + } + } + + .actor-info { + justify-content: center; + display: inline-flex; + flex-direction: column; + + .actor-info-names { + display: flex; + align-items: center; + + .actor-info-display-name { + font-size: 20px; + font-weight: $font-bold; + + @media screen and (max-width: $small-view) { + font-size: 16px; + } + } + + .actor-info-username { + margin-left: 7px; + position: relative; + top: 2px; + font-size: 14px; + color: $grey-actor-name; + } + } + + .actor-info-followers { + font-size: 15px; + padding-bottom: .5rem; + } + } +} diff --git a/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts b/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts new file mode 100644 index 000000000..0c04ae4a6 --- /dev/null +++ b/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts @@ -0,0 +1,64 @@ +import { BytesPipe } from 'ngx-pipes' +import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' +import { Notifier, ServerService } from '@app/core' +import { Account, VideoChannel } from '@app/shared/shared-main' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { ServerConfig } from '@shared/models' + +@Component({ + selector: 'my-actor-avatar-info', + templateUrl: './actor-avatar-info.component.html', + styleUrls: [ './actor-avatar-info.component.scss' ] +}) +export class ActorAvatarInfoComponent implements OnInit { + @ViewChild('avatarfileInput') avatarfileInput: ElementRef + + @Input() actor: VideoChannel | Account + + @Output() avatarChange = new EventEmitter() + + maxSizeText: string + + private serverConfig: ServerConfig + private bytesPipe: BytesPipe + + constructor ( + private serverService: ServerService, + private notifier: Notifier, + private i18n: I18n + ) { + this.bytesPipe = new BytesPipe() + this.maxSizeText = this.i18n('max size') + } + + ngOnInit (): void { + this.serverConfig = this.serverService.getTmpConfig() + this.serverService.getConfig() + .subscribe(config => this.serverConfig = config) + } + + onAvatarChange () { + const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ] + if (avatarfile.size > this.maxAvatarSize) { + this.notifier.error('Error', 'This image is too large.') + return + } + + const formData = new FormData() + formData.append('avatarfile', avatarfile) + + this.avatarChange.emit(formData) + } + + get maxAvatarSize () { + return this.serverConfig.avatar.file.size.max + } + + get maxAvatarSizeInBytes () { + return this.bytesPipe.transform(this.maxAvatarSize) + } + + get avatarExtensions () { + return this.serverConfig.avatar.file.extensions.join(', ') + } +} diff --git a/client/src/app/shared/shared-main/account/actor.model.ts b/client/src/app/shared/shared-main/account/actor.model.ts new file mode 100644 index 000000000..5fc7989dd --- /dev/null +++ b/client/src/app/shared/shared-main/account/actor.model.ts @@ -0,0 +1,65 @@ +import { Actor as ActorServer, Avatar } from '@shared/models' +import { getAbsoluteAPIUrl } from '@app/helpers' + +export abstract class Actor implements ActorServer { + id: number + url: string + name: string + host: string + followingCount: number + followersCount: number + createdAt: Date | string + updatedAt: Date | string + avatar: Avatar + + avatarUrl: string + + static GET_ACTOR_AVATAR_URL (actor: { avatar?: Avatar }) { + if (actor?.avatar?.url) return actor.avatar.url + + if (actor && actor.avatar) { + const absoluteAPIUrl = getAbsoluteAPIUrl() + + return absoluteAPIUrl + actor.avatar.path + } + + return this.GET_DEFAULT_AVATAR_URL() + } + + static GET_DEFAULT_AVATAR_URL () { + return window.location.origin + '/client/assets/images/default-avatar.png' + } + + static CREATE_BY_STRING (accountName: string, host: string, forceHostname = false) { + const absoluteAPIUrl = getAbsoluteAPIUrl() + const thisHost = new URL(absoluteAPIUrl).host + + if (host.trim() === thisHost && !forceHostname) return accountName + + return accountName + '@' + host + } + + protected constructor (hash: ActorServer) { + this.id = hash.id + this.url = hash.url + this.name = hash.name + this.host = hash.host + this.followingCount = hash.followingCount + this.followersCount = hash.followersCount + this.createdAt = new Date(hash.createdAt.toString()) + this.updatedAt = new Date(hash.updatedAt.toString()) + this.avatar = hash.avatar + + this.updateComputedAttributes() + } + + updateAvatar (newAvatar: Avatar) { + this.avatar = newAvatar + + this.updateComputedAttributes() + } + + private updateComputedAttributes () { + this.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(this) + } +} diff --git a/client/src/app/shared/shared-main/account/avatar.component.html b/client/src/app/shared/shared-main/account/avatar.component.html new file mode 100644 index 000000000..09871fca4 --- /dev/null +++ b/client/src/app/shared/shared-main/account/avatar.component.html @@ -0,0 +1,8 @@ + diff --git a/client/src/app/shared/shared-main/account/avatar.component.scss b/client/src/app/shared/shared-main/account/avatar.component.scss new file mode 100644 index 000000000..37709fce6 --- /dev/null +++ b/client/src/app/shared/shared-main/account/avatar.component.scss @@ -0,0 +1,40 @@ +@import '_mixins'; + +.wrapper { + $avatar-size: 35px; + + width: $avatar-size; + height: $avatar-size; + position: relative; + margin-right: 5px; + margin-bottom: 5px; + + &.avatar-sm { + width: 28px; + height: 28px; + margin-bottom: 3px; + } + + a { + @include disable-outline; + } + + a img { + height: 100%; + object-fit: cover; + position: absolute; + top:50%; + left:50%; + border-radius: 50%; + transform: translate(-50%,-50%) + } + + a:nth-of-type(2) img { + height: 60%; + width: 60%; + border: 2px solid pvar(--mainBackgroundColor); + transform: translateX(15%); + position: relative; + background-color: pvar(--mainBackgroundColor); + } +} diff --git a/client/src/app/shared/shared-main/account/avatar.component.ts b/client/src/app/shared/shared-main/account/avatar.component.ts new file mode 100644 index 000000000..31f39c200 --- /dev/null +++ b/client/src/app/shared/shared-main/account/avatar.component.ts @@ -0,0 +1,31 @@ +import { Component, Input, OnInit } from '@angular/core' +import { Video } from '../video/video.model' +import { I18n } from '@ngx-translate/i18n-polyfill' + +@Component({ + selector: 'avatar-channel', + templateUrl: './avatar.component.html', + styleUrls: [ './avatar.component.scss' ] +}) +export class AvatarComponent implements OnInit { + @Input() video: Video + @Input() size: 'md' | 'sm' = 'md' + + channelLinkTitle = '' + accountLinkTitle = '' + + constructor ( + private i18n: I18n + ) {} + + ngOnInit () { + this.channelLinkTitle = this.i18n( + '{{name}} (channel page)', + { name: this.video.channel.name, handle: this.video.byVideoChannel } + ) + this.accountLinkTitle = this.i18n( + '{{name}} (account page)', + { name: this.video.account.name, handle: this.video.byAccount } + ) + } +} diff --git a/client/src/app/shared/shared-main/account/index.ts b/client/src/app/shared/shared-main/account/index.ts new file mode 100644 index 000000000..f5b9f3634 --- /dev/null +++ b/client/src/app/shared/shared-main/account/index.ts @@ -0,0 +1,5 @@ +export * from './account.model' +export * from './account.service' +export * from './actor-avatar-info.component' +export * from './actor.model' +export * from './avatar.component' -- cgit v1.2.3