diff options
Diffstat (limited to 'client/src/app/shared/shared-actor-image')
5 files changed, 254 insertions, 0 deletions
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.html b/client/src/app/shared/shared-actor-image/actor-avatar.component.html new file mode 100644 index 000000000..607f28e5b --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.html | |||
@@ -0,0 +1,19 @@ | |||
1 | <ng-template #img> | ||
2 | <img *ngIf="previewImage || avatarUrl || !initial" [class]="class" [src]="previewImage || avatarUrl || defaultAvatarUrl" [alt]="alt" /> | ||
3 | |||
4 | <div *ngIf="!avatarUrl && initial" [class]="class"> | ||
5 | <span>{{ initial }}</span> | ||
6 | </div> | ||
7 | </ng-template> | ||
8 | |||
9 | <a *ngIf="hasActor() && href" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title"> | ||
10 | <ng-template *ngTemplateOutlet="img"></ng-template> | ||
11 | </a> | ||
12 | |||
13 | <a *ngIf="hasActor() && internalHref" [routerLink]="internalHref" [title]="title"> | ||
14 | <ng-template *ngTemplateOutlet="img"></ng-template> | ||
15 | </a> | ||
16 | |||
17 | <ng-container *ngIf="!hasActor() || (!href && !internalHref)"> | ||
18 | <ng-template *ngTemplateOutlet="img"></ng-template> | ||
19 | </ng-container> | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.scss b/client/src/app/shared/shared-actor-image/actor-avatar.component.scss new file mode 100644 index 000000000..f014dec48 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.scss | |||
@@ -0,0 +1,101 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .avatar { | ||
5 | --avatarSize: 100%; | ||
6 | --initialFontSize: 22px; | ||
7 | |||
8 | width: var(--avatarSize); | ||
9 | height: var(--avatarSize); | ||
10 | min-width: var(--avatarSize); | ||
11 | min-height: var(--avatarSize); | ||
12 | |||
13 | &.account { | ||
14 | object-fit: cover; | ||
15 | border-radius: 50%; | ||
16 | } | ||
17 | |||
18 | &.channel { | ||
19 | border-radius: 5px; | ||
20 | } | ||
21 | } | ||
22 | |||
23 | .avatar-18 { | ||
24 | --avatarSize: 18px; | ||
25 | --initialFontSize: 13px; | ||
26 | } | ||
27 | |||
28 | .avatar-25 { | ||
29 | --avatarSize: 25px; | ||
30 | } | ||
31 | |||
32 | .avatar-32 { | ||
33 | --avatarSize: 32px; | ||
34 | } | ||
35 | |||
36 | .avatar-34 { | ||
37 | --avatarSize: 34px; | ||
38 | } | ||
39 | |||
40 | .avatar-36 { | ||
41 | --avatarSize: 36px; | ||
42 | } | ||
43 | |||
44 | .avatar-40 { | ||
45 | --avatarSize: 40px; | ||
46 | } | ||
47 | |||
48 | .avatar-100 { | ||
49 | --avatarSize: 100px; | ||
50 | --initialFontSize: 40px; | ||
51 | } | ||
52 | |||
53 | .avatar-120 { | ||
54 | --avatarSize: 120px; | ||
55 | --initialFontSize: 46px; | ||
56 | } | ||
57 | |||
58 | a:hover { | ||
59 | text-decoration: none; | ||
60 | } | ||
61 | |||
62 | .initial { | ||
63 | background-color: #3C2109; | ||
64 | color: #fff; | ||
65 | display: flex; | ||
66 | align-items: center; | ||
67 | justify-content: center; | ||
68 | font-size: var(--initialFontSize); | ||
69 | |||
70 | &.blue { | ||
71 | background-color: #009FD4; | ||
72 | } | ||
73 | |||
74 | &.green { | ||
75 | background-color: #00AA55; | ||
76 | } | ||
77 | |||
78 | &.purple { | ||
79 | background-color: #B381B3; | ||
80 | } | ||
81 | |||
82 | &.gray { | ||
83 | background-color: #939393; | ||
84 | } | ||
85 | |||
86 | &.yellow { | ||
87 | background-color: #AA8F00; | ||
88 | } | ||
89 | |||
90 | &.orange { | ||
91 | background-color: #D47500; | ||
92 | } | ||
93 | |||
94 | &.red { | ||
95 | background-color: #E76E3C; | ||
96 | } | ||
97 | |||
98 | &.dark-blue { | ||
99 | background-color: #0A3055; | ||
100 | } | ||
101 | } | ||
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 new file mode 100644 index 000000000..6bb3b65fa --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts | |||
@@ -0,0 +1,110 @@ | |||
1 | import { Component, Input } from '@angular/core' | ||
2 | import { SafeResourceUrl } from '@angular/platform-browser' | ||
3 | import { VideoChannel } from '../shared-main' | ||
4 | import { Account } from '../shared-main/account/account.model' | ||
5 | |||
6 | type ActorInput = { | ||
7 | name: string | ||
8 | avatar?: { url?: string, path: string } | ||
9 | url: string | ||
10 | } | ||
11 | |||
12 | @Component({ | ||
13 | selector: 'my-actor-avatar', | ||
14 | styleUrls: [ './actor-avatar.component.scss' ], | ||
15 | templateUrl: './actor-avatar.component.html' | ||
16 | }) | ||
17 | export class ActorAvatarComponent { | ||
18 | @Input() account: ActorInput | ||
19 | @Input() channel: ActorInput | ||
20 | |||
21 | @Input() previewImage: SafeResourceUrl | ||
22 | |||
23 | @Input() size: '18' | '25' | '32' | '34' | '36' | '40' | '100' | '120' | ||
24 | |||
25 | // Use an external link | ||
26 | @Input() href: string | ||
27 | // Use routerLink | ||
28 | @Input() internalHref: string | any[] | ||
29 | |||
30 | @Input() set title (value) { | ||
31 | this._title = value | ||
32 | } | ||
33 | |||
34 | private _title: string | ||
35 | |||
36 | get title () { | ||
37 | if (this._title) return this._title | ||
38 | if (this.account) return $localize`${this.account.name} (account page)` | ||
39 | if (this.channel) return $localize`${this.channel.name} (channel page)` | ||
40 | |||
41 | return '' | ||
42 | } | ||
43 | |||
44 | get alt () { | ||
45 | if (this.account) return $localize`Account avatar` | ||
46 | if (this.channel) return $localize`Channel avatar` | ||
47 | |||
48 | return '' | ||
49 | } | ||
50 | |||
51 | get class () { | ||
52 | const base = [ 'avatar' ] | ||
53 | |||
54 | if (this.size) base.push(`avatar-${this.size}`) | ||
55 | |||
56 | if (this.account) base.push('account') | ||
57 | else base.push('channel') | ||
58 | |||
59 | if (this.initial) { | ||
60 | base.push('initial') | ||
61 | base.push(this.getColorTheme()) | ||
62 | } | ||
63 | |||
64 | return base | ||
65 | } | ||
66 | |||
67 | get defaultAvatarUrl () { | ||
68 | if (this.account) Account.GET_DEFAULT_AVATAR_URL() | ||
69 | if (this.channel) return VideoChannel.GET_DEFAULT_AVATAR_URL() | ||
70 | |||
71 | return '' | ||
72 | } | ||
73 | |||
74 | get avatarUrl () { | ||
75 | if (this.account) return Account.GET_ACTOR_AVATAR_URL(this.account) | ||
76 | if (this.channel) return VideoChannel.GET_ACTOR_AVATAR_URL(this.account) | ||
77 | |||
78 | return '' | ||
79 | } | ||
80 | |||
81 | get initial () { | ||
82 | const name = this.account?.name | ||
83 | if (!name) return '' | ||
84 | |||
85 | return name.slice(0, 1) | ||
86 | } | ||
87 | |||
88 | hasActor () { | ||
89 | return !!this.account || !!this.channel | ||
90 | } | ||
91 | |||
92 | private getColorTheme () { | ||
93 | // Keep consistency with CSS | ||
94 | const themes = { | ||
95 | abc: 'blue', | ||
96 | def: 'green', | ||
97 | ghi: 'purple', | ||
98 | jkl: 'gray', | ||
99 | mno: 'yellow', | ||
100 | pqr: 'orange', | ||
101 | stv: 'red', | ||
102 | wxyz: 'dark-blue' | ||
103 | } | ||
104 | |||
105 | const theme = Object.keys(themes) | ||
106 | .find(chars => chars.includes(this.initial)) | ||
107 | |||
108 | return themes[theme] | ||
109 | } | ||
110 | } | ||
diff --git a/client/src/app/shared/shared-actor-image/index.ts b/client/src/app/shared/shared-actor-image/index.ts new file mode 100644 index 000000000..18a9038eb --- /dev/null +++ b/client/src/app/shared/shared-actor-image/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './shared-actor-image.module' | |||
diff --git a/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts new file mode 100644 index 000000000..8ea4bb2bf --- /dev/null +++ b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts | |||
@@ -0,0 +1,23 @@ | |||
1 | |||
2 | import { NgModule } from '@angular/core' | ||
3 | import { SharedGlobalIconModule } from '../shared-icons' | ||
4 | import { SharedMainModule } from '../shared-main/shared-main.module' | ||
5 | import { ActorAvatarComponent } from './actor-avatar.component' | ||
6 | |||
7 | @NgModule({ | ||
8 | imports: [ | ||
9 | SharedMainModule, | ||
10 | SharedGlobalIconModule | ||
11 | ], | ||
12 | |||
13 | declarations: [ | ||
14 | ActorAvatarComponent | ||
15 | ], | ||
16 | |||
17 | exports: [ | ||
18 | ActorAvatarComponent | ||
19 | ], | ||
20 | |||
21 | providers: [ ] | ||
22 | }) | ||
23 | export class SharedActorImageModule { } | ||