diff --git a/client/src/app/modules/core/menu/menu.component.html b/client/src/app/modules/core/menu/menu.component.html
index 5c340297..cbca1cbd 100644
--- a/client/src/app/modules/core/menu/menu.component.html
+++ b/client/src/app/modules/core/menu/menu.component.html
@@ -49,7 +49,6 @@
data-cy="auth-menu-button"
styleClass="mr-2"
size="normal"
- shape="circle"
/>
}
diff --git a/client/src/app/modules/core/searchable/searchable.component.html b/client/src/app/modules/core/searchable/searchable.component.html
index 9316c95a..4c30c3f2 100644
--- a/client/src/app/modules/core/searchable/searchable.component.html
+++ b/client/src/app/modules/core/searchable/searchable.component.html
@@ -57,7 +57,6 @@
"
styleClass="mr-2"
size="normal"
- shape="circle"
>
{{ searchable.area.name }}
@@ -92,7 +91,6 @@
"
styleClass="mr-2"
size="normal"
- shape="circle"
>
{{ searchable.sector.name }}
@@ -122,7 +120,6 @@
"
styleClass="mr-2"
size="normal"
- shape="circle"
>
{{ searchable.crag.name }}
@@ -143,7 +140,6 @@
[user]="searchable.user"
styleClass="mr-2"
size="normal"
- shape="circle"
/>
{{ searchable.user.fullname }}
diff --git a/client/src/app/modules/ranking/ranking-list/ranking-list.component.html b/client/src/app/modules/ranking/ranking-list/ranking-list.component.html
index c81dec7b..1f3dce35 100644
--- a/client/src/app/modules/ranking/ranking-list/ranking-list.component.html
+++ b/client/src/app/modules/ranking/ranking-list/ranking-list.component.html
@@ -123,7 +123,6 @@
[user]="ranking.user"
styleClass="mr-2"
size="large"
- shape="circle"
/>
+
+
+
+
+} @else if (imageUrl) {
@@ -11,7 +24,7 @@
[label]="initials"
[style]="gradientStyle"
[size]="size"
- [shape]="shape"
+ shape="circle"
[styleClass]="fallbackClasses"
[attr.aria-label]="ariaLabel"
/>
diff --git a/client/src/app/modules/shared/components/user-avatar/user-avatar.component.scss b/client/src/app/modules/shared/components/user-avatar/user-avatar.component.scss
index 054ff1fa..07dc845b 100644
--- a/client/src/app/modules/shared/components/user-avatar/user-avatar.component.scss
+++ b/client/src/app/modules/shared/components/user-avatar/user-avatar.component.scss
@@ -3,3 +3,43 @@
font-weight: 600;
border: none;
}
+
+:host ::ng-deep p-image.lc-user-avatar-preview {
+ display: inline-flex;
+ width: fit-content;
+ cursor: pointer;
+ line-height: 0;
+ position: relative;
+ border-radius: 50%;
+ overflow: hidden;
+
+ > img {
+ display: block;
+ object-fit: cover;
+ border-radius: 50%;
+ }
+
+ &.lc-user-avatar-preview--normal > img {
+ width: 2rem;
+ height: 2rem;
+ }
+
+ &.lc-user-avatar-preview--large > img {
+ width: 3rem;
+ height: 3rem;
+ }
+
+ &.lc-user-avatar-preview--xlarge > img {
+ width: 4rem;
+ height: 4rem;
+ }
+
+ .p-image-preview-mask {
+ position: absolute;
+ inset: 0;
+ width: 100% !important;
+ height: 100% !important;
+ border-radius: 50%;
+ margin: 0;
+ }
+}
diff --git a/client/src/app/modules/shared/components/user-avatar/user-avatar.component.ts b/client/src/app/modules/shared/components/user-avatar/user-avatar.component.ts
index 4e63a8e6..094aa362 100644
--- a/client/src/app/modules/shared/components/user-avatar/user-avatar.component.ts
+++ b/client/src/app/modules/shared/components/user-avatar/user-avatar.component.ts
@@ -6,15 +6,32 @@ import {
SimpleChanges,
} from '@angular/core';
import { AvatarModule } from 'primeng/avatar';
+import { ImageModule } from 'primeng/image';
import { User } from '../../../../models/user';
import {
userAvatarGradientCss,
userInitials,
} from '../../../../utility/misc/user-avatar-gradient';
+/**
+ * Renders a user avatar using one of three template branches:
+ *
+ * 1. `p-image` (preview) — when `[preview]` is true and the user has an avatar file.
+ * PrimeNG Avatar has no fullscreen preview; `p-image` provides the same click-to-zoom
+ * behaviour as gallery images. Both `[src]` and `[previewImageSrc]` are set to
+ * `thumbnailXL`: the former for the inline circle, the latter for the fullscreen overlay
+ * (PrimeNG falls back to `[src]` when `[previewImageSrc]` is omitted).
+ *
+ * 2. `p-avatar` with `[image]` — default when the user has an avatar and preview is off.
+ * Uses the configurable `thumbnail` input (usually `thumbnailS` or `thumbnailM`) so lists
+ * stay lightweight.
+ *
+ * 3. `p-avatar` with `[label]` — when there is no image URL (no avatar and no imageFallback).
+ * Shows generated initials on a deterministic gradient background.
+ */
@Component({
selector: 'lc-user-avatar',
- imports: [AvatarModule],
+ imports: [AvatarModule, ImageModule],
templateUrl: './user-avatar.component.html',
styleUrl: './user-avatar.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -23,12 +40,16 @@ export class UserAvatarComponent implements OnChanges {
@Input() user: User | null | undefined;
@Input() thumbnail: 'thumbnailS' | 'thumbnailM' = 'thumbnailS';
@Input() size: 'normal' | 'large' | 'xlarge' = 'normal';
- @Input() shape: 'circle' | 'square' = 'circle';
@Input() styleClass = '';
/** When set (e.g. deleted comment), always show this image and ignore avatar / initials. */
@Input() imageFallback: string | null | undefined;
+ /** Opens a fullscreen image preview when the user has an avatar. */
+ @Input() preview = false;
imageUrl: string | null = null;
+ previewImageUrl: string | null = null;
+ previewEnabled = false;
+ previewStyleClass = '';
initials = '';
gradientStyle: Record = {};
fallbackClasses = '';
@@ -51,5 +72,13 @@ export class UserAvatarComponent implements OnChanges {
.join(' ');
this.ariaLabel =
u?.fullname?.trim() || `${first} ${last}`.trim() || this.initials;
+ this.previewImageUrl = u?.avatar?.thumbnailXL ?? null;
+ this.previewEnabled = this.preview && !!this.previewImageUrl;
+ const previewClasses = [
+ 'lc-user-avatar-preview',
+ `lc-user-avatar-preview--${this.size}`,
+ this.styleClass,
+ ];
+ this.previewStyleClass = previewClasses.filter(Boolean).join(' ');
}
}
diff --git a/client/src/app/modules/user/user-detail/user-detail.component.html b/client/src/app/modules/user/user-detail/user-detail.component.html
index ef2b5b66..af7f59e9 100644
--- a/client/src/app/modules/user/user-detail/user-detail.component.html
+++ b/client/src/app/modules/user/user-detail/user-detail.component.html
@@ -6,8 +6,8 @@
}
{{ user?.firstname }} {{ user?.lastname }}
diff --git a/client/src/app/modules/user/user-list/user-list.component.html b/client/src/app/modules/user/user-list/user-list.component.html
index 5e75e3f5..cd240e91 100644
--- a/client/src/app/modules/user/user-list/user-list.component.html
+++ b/client/src/app/modules/user/user-list/user-list.component.html
@@ -65,7 +65,6 @@
[user]="user"
thumbnail="thumbnailM"
size="xlarge"
- shape="circle"
styleClass="mr-2"
/>