feat(web): display number of likes in asset viewer (#18911)

* feat: display number of likes

* fix: properly decrement like count on unlike

Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>

* chore: pr feedback

* chore: updated related test

* chore: formatter run

* chore: force numberOfLikes to null in album context to pass lint

* chore: open-api updated

* fix: use undefined, not null

* styling tweaks

* chore: updated sql

---------

Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Robin Brisa
2025-06-04 19:41:50 +02:00
committed by GitHub
parent 5d0ad853f4
commit a26d703335
13 changed files with 77 additions and 29 deletions

View File

@@ -7,25 +7,29 @@
interface Props {
isLiked: ActivityResponseDto | null;
numberOfComments: number | undefined;
numberOfLikes: number | undefined;
disabled: boolean;
onOpenActivityTab: () => void;
onFavorite: () => void;
}
let { isLiked, numberOfComments, disabled, onOpenActivityTab, onFavorite }: Props = $props();
let { isLiked, numberOfComments, numberOfLikes, disabled, onOpenActivityTab, onFavorite }: Props = $props();
</script>
<div class="w-full flex p-4 text-white items-center justify-center rounded-full gap-5 bg-immich-dark-bg bg-opacity-60">
<div class="w-full flex p-4 items-center justify-center rounded-full gap-5 bg-subtle border bg-opacity-60">
<button type="button" class={disabled ? 'cursor-not-allowed' : ''} onclick={onFavorite} {disabled}>
<div class="items-center justify-center">
<Icon path={isLiked ? mdiHeart : mdiHeartOutline} size={24} />
<div class="flex gap-2 items-center justify-center">
<Icon path={isLiked ? mdiHeart : mdiHeartOutline} size={24} class={isLiked ? 'text-red-400' : 'text-fg'} />
{#if numberOfLikes}
<div class="text-l">{numberOfLikes.toLocaleString($locale)}</div>
{/if}
</div>
</button>
<button type="button" onclick={onOpenActivityTab}>
<div class="flex gap-2 items-center justify-center">
<Icon path={mdiCommentOutline} class="scale-x-[-1]" size={24} />
{#if numberOfComments}
<div class="text-xl">{numberOfComments.toLocaleString($locale)}</div>
<div class="text-l">{numberOfComments.toLocaleString($locale)}</div>
{/if}
</div>
</button>

View File

@@ -118,12 +118,9 @@
};
</script>
<div class="overflow-y-hidden relative h-full" bind:offsetHeight={innerHeight}>
<div class="dark:bg-immich-dark-bg dark:text-immich-dark-fg w-full h-full">
<div
class="flex w-full h-fit dark:bg-immich-dark-bg dark:text-immich-dark-fg p-2 bg-white"
bind:clientHeight={activityHeight}
>
<div class="overflow-y-hidden relative h-full border-l border-subtle bg-subtle" bind:offsetHeight={innerHeight}>
<div class="w-full h-full">
<div class="flex w-full h-fit dark:text-immich-dark-fg p-2 bg-subtle" bind:clientHeight={activityHeight}>
<div class="flex place-items-center gap-2">
<IconButton
shape="round"

View File

@@ -513,6 +513,7 @@
disabled={!album?.isActivityEnabled}
isLiked={activityManager.isLiked}
numberOfComments={activityManager.commentCount}
numberOfLikes={activityManager.likeCount}
onFavorite={handleFavorite}
onOpenActivityTab={handleOpenActivity}
/>

View File

@@ -17,6 +17,7 @@ class ActivityManager {
#assetId = $state<string | undefined>();
#activities = $state<ActivityResponseDto[]>([]);
#commentCount = $state(0);
#likeCount = $state(0);
#isLiked = $state<ActivityResponseDto | null>(null);
get activities() {
@@ -27,6 +28,10 @@ class ActivityManager {
return this.#commentCount;
}
get likeCount() {
return this.#likeCount;
}
get isLiked() {
return this.#isLiked;
}
@@ -48,6 +53,10 @@ class ActivityManager {
this.#commentCount++;
}
if (activity.type === ReactionType.Like) {
this.#likeCount++;
}
handlePromiseError(this.refreshActivities(this.#albumId, this.#assetId));
return activity;
}
@@ -61,6 +70,10 @@ class ActivityManager {
this.#commentCount--;
}
if (activity.type === ReactionType.Like) {
this.#likeCount--;
}
this.#activities = index
? this.#activities.splice(index, 1)
: this.#activities.filter(({ id }) => id !== activity.id);
@@ -98,8 +111,9 @@ class ActivityManager {
});
this.#isLiked = liked ?? null;
const { comments } = await getActivityStatistics({ albumId, assetId });
const { comments, likes } = await getActivityStatistics({ albumId, assetId });
this.#commentCount = comments;
this.#likeCount = likes;
}
reset() {
@@ -107,6 +121,7 @@ class ActivityManager {
this.#assetId = undefined;
this.#activities = [];
this.#commentCount = 0;
this.#likeCount = 0;
}
}

View File

@@ -576,6 +576,7 @@
disabled={!album.isActivityEnabled}
isLiked={activityManager.isLiked}
numberOfComments={activityManager.commentCount}
numberOfLikes={undefined}
onFavorite={handleFavorite}
onOpenActivityTab={handleOpenAndCloseActivityTab}
/>