Skip to content

Commit 19bb92e

Browse files
authored
Merge pull request #185 from Toastbrot236/user-heart
Ability to heart users
2 parents 6fca2f9 + b6e9fb5 commit 19bb92e

File tree

6 files changed

+143
-11
lines changed

6 files changed

+143
-11
lines changed

src/app/api/client.service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,12 @@ export class ClientService extends ApiImplementation {
182182
uploadAsset(hash: string, data: ArrayBuffer) {
183183
return this.http.post<Asset>(`/assets/${hash}`, data);
184184
}
185+
186+
setUserAsHearted(uuid: string) {
187+
return this.http.post<Response>(`/users/uuid/${uuid}/heart`, null);
188+
}
189+
190+
setUserAsUnhearted(uuid: string) {
191+
return this.http.post<Response>(`/users/uuid/${uuid}/unheart`, null);
192+
}
185193
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface UserRelations {
2+
isHearted: boolean;
3+
}

src/app/api/types/users/user.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {Room} from "../rooms/room";
2+
import { UserRelations } from "./user-relations";
23
import {UserStatistics} from "./user-statistics";
34

45
export interface User {
@@ -13,4 +14,5 @@ export interface User {
1314

1415
statistics: UserStatistics;
1516
activeRoom: Room | undefined;
17+
ownRelations: UserRelations | undefined;
1618
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { Component, Input, TemplateRef, ViewChild } from "@angular/core";
2+
import { BannerService } from "../../../banners/banner.service";
3+
import { ExtendedUser } from "../../../api/types/users/extended-user";
4+
import { ClientService } from "../../../api/client.service";
5+
import { ButtonOrNavItemComponent } from "../form/button-or-navitem.component";
6+
import { faHeart, faHeartCrack } from "@fortawesome/free-solid-svg-icons";
7+
import { FancyHeaderButtonsComponent } from "./fancy-header-buttons.component";
8+
import { User } from "../../../api/types/users/user";
9+
import { UserRelations } from "../../../api/types/users/user-relations";
10+
import { RefreshApiError } from "../../../api/refresh-api-error";
11+
12+
@Component({
13+
selector: 'app-fancy-header-user-buttons',
14+
imports: [
15+
ButtonOrNavItemComponent,
16+
FancyHeaderButtonsComponent,
17+
],
18+
template: `
19+
<ng-template #heartButtonTemplate let-templateHasText="hasText" let-templateIsNavItem="isNavItem">
20+
<app-button-or-navitem
21+
text="Heart"
22+
[icon]="faHeart"
23+
color="bg-pink"
24+
25+
textAlt="Unheart"
26+
[iconAlt]="faHeartCrack"
27+
colorAlt="bg-secondary"
28+
29+
[state]="relations.isHearted"
30+
(click)="heartButtonClick()"
31+
32+
[hasText]="templateHasText"
33+
[isNavItem]="templateIsNavItem">
34+
</app-button-or-navitem>
35+
</ng-template>
36+
37+
@if (buttonsInitialized) {
38+
<app-fancy-header-buttons
39+
[buttonTemplateRefs]="buttonTemplateRefs">
40+
</app-fancy-header-buttons>
41+
}
42+
`,
43+
styles: ``
44+
})
45+
46+
export class FancyHeaderUserButtonsComponent {
47+
@Input({required: true}) public targetUser: User = undefined!;
48+
@Input({required: true}) public ownUser: ExtendedUser = undefined!;
49+
@Input({required: true}) public relations: UserRelations = undefined!;
50+
51+
@ViewChild('heartButtonTemplate') heartButtonTemplateRef!: TemplateRef<any>;
52+
53+
buttonTemplateRefs: TemplateRef<any>[] = [];
54+
buttonsInitialized: boolean = false;
55+
56+
constructor(private client: ClientService, private bannerService: BannerService) {}
57+
58+
ngAfterViewInit() {
59+
// Heart Button
60+
if (this.ownUser.userId !== this.targetUser.userId) {
61+
this.buttonTemplateRefs.push(this.heartButtonTemplateRef);
62+
}
63+
64+
// TODO: Other buttons, such as an edit button for mods
65+
66+
this.buttonsInitialized = true;
67+
}
68+
69+
async heartButtonClick() {
70+
if (this.relations.isHearted) {
71+
this.client.setUserAsUnhearted(this.targetUser.userId).subscribe({
72+
error: error => {
73+
const apiError: RefreshApiError | undefined = error.error?.error;
74+
this.bannerService.warn("Failed to unheart user", apiError == null ? error.message : apiError.message);
75+
},
76+
next: _ => {
77+
this.relations.isHearted = false;
78+
}
79+
});
80+
}
81+
else {
82+
this.client.setUserAsHearted(this.targetUser.userId).subscribe({
83+
error: error => {
84+
const apiError: RefreshApiError | undefined = error.error?.error;
85+
this.bannerService.warn("Failed to heart user", apiError == null ? error.message : apiError.message);
86+
},
87+
next: _ => {
88+
this.relations.isHearted = true;
89+
}
90+
});
91+
}
92+
}
93+
94+
protected readonly faHeart = faHeart;
95+
protected readonly faHeartCrack = faHeartCrack;
96+
}

src/app/pages/user/user.component.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,11 @@
1111
<app-user-status [activeRoom]="user.activeRoom" [lastSeen]="user.lastLoginDate"></app-user-status>
1212
</ng-container>
1313
<app-user-statistics [stats]="user.statistics" statistics></app-user-statistics>
14+
@if(ownUser && relations && !isMobile) {
15+
<app-fancy-header-user-buttons [targetUser]="user" [ownUser]="ownUser" [relations]="relations" buttonArea></app-fancy-header-user-buttons>
16+
}
17+
@else if(ownUser && relations && isMobile) {
18+
<app-fancy-header-user-buttons [targetUser]="user" [ownUser]="ownUser" [relations]="relations" buttonAreaMobile></app-fancy-header-user-buttons>
19+
}
1420
</app-fancy-header>
1521
}

src/app/pages/user/user.component.ts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,49 @@ import {FancyHeaderComponent} from "../../components/ui/layouts/fancy-header.com
1111
import {LayoutService} from "../../services/layout.service";
1212
import {UserStatusComponent} from "../../components/ui/info/user-status.component";
1313
import {UserStatisticsComponent} from "../../components/items/user-statistics.component";
14+
import { UserRelations } from '../../api/types/users/user-relations';
15+
import { FancyHeaderUserButtonsComponent } from "../../components/ui/layouts/fancy-header-user-buttons.component";
16+
import { ExtendedUser } from '../../api/types/users/extended-user';
17+
import { AuthenticationService } from '../../api/authentication.service';
1418

1519
@Component({
1620
selector: 'app-user',
1721
imports: [
18-
DefaultPipe,
19-
UserAvatarComponent,
20-
DateComponent,
21-
FancyHeaderComponent,
22-
AsyncPipe,
23-
UserStatusComponent,
24-
UserStatisticsComponent
25-
],
22+
DefaultPipe,
23+
UserAvatarComponent,
24+
DateComponent,
25+
FancyHeaderComponent,
26+
AsyncPipe,
27+
UserStatusComponent,
28+
UserStatisticsComponent,
29+
FancyHeaderUserButtonsComponent
30+
],
2631
templateUrl: './user.component.html',
2732
styles: ``
2833
})
2934
export class UserComponent {
3035
user: User | undefined | null;
36+
relations: UserRelations | undefined;
37+
protected ownUser: ExtendedUser | undefined;
38+
protected isMobile: boolean = false;
3139

32-
constructor(private title: TitleService, private client: ClientService, route: ActivatedRoute, protected layout: LayoutService) {
40+
constructor(private auth: AuthenticationService, private client: ClientService, route: ActivatedRoute, protected layout: LayoutService) {
3341
route.params.subscribe(params => {
3442
const username: string | undefined = params['username'];
3543
const uuid: string | undefined = params['uuid'];
3644

3745
this.client.getUserByEitherLookup(username, uuid).subscribe(user => {
3846
this.user = user;
39-
})
40-
})
47+
this.relations = user.ownRelations;
48+
49+
this.auth.user.subscribe(user => {
50+
if(user) {
51+
this.ownUser = user;
52+
}
53+
});
54+
});
55+
});
56+
57+
this.layout.isMobile.subscribe(v => this.isMobile = v);
4158
}
4259
}

0 commit comments

Comments
 (0)