mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-07-22 07:30:18 -04:00
fix: Simplify rating logic (#7918)
This commit is contained in:
@@ -6,8 +6,8 @@
|
|||||||
class="star"
|
class="star"
|
||||||
:class="{
|
:class="{
|
||||||
'star-half': star === 'half',
|
'star-half': star === 'half',
|
||||||
'text-secondary': !useGroupStyle,
|
'text-secondary': !showGroupAverage,
|
||||||
'text-grey-darken-1': useGroupStyle,
|
'text-grey-darken-1': showGroupAverage,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- We render both the full and empty stars for "half" stars because they're layered over each other -->
|
<!-- We render both the full and empty stars for "half" stars because they're layered over each other -->
|
||||||
@@ -28,7 +28,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
|
||||||
import { useUserSelfRatings } from "~/composables/use-users";
|
import { useUserSelfRatings } from "~/composables/use-users";
|
||||||
|
|
||||||
type Star = "full" | "half" | "empty";
|
type Star = "full" | "half" | "empty";
|
||||||
@@ -44,15 +43,17 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { isOwnGroup } = useLoggedInState();
|
|
||||||
const { userRatings } = useUserSelfRatings();
|
const { userRatings } = useUserSelfRatings();
|
||||||
|
|
||||||
const userRating = computed(() => {
|
const userRating = computed(() => {
|
||||||
return userRatings.value.find(r => r.recipeId === props.recipeId)?.rating ?? undefined;
|
return userRatings.value.find(r => r.recipeId === props.recipeId)?.rating ?? null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const ratingValue = computed(() => userRating.value || props.modelValue || 0);
|
// this display is always readonly, so we show the user's own rating if they have one,
|
||||||
const useGroupStyle = computed(() => isOwnGroup.value && !userRating.value && props.modelValue);
|
// and otherwise fall back to the group average (in grey), if there is one.
|
||||||
|
// An unset rating may be null or 0
|
||||||
|
const showGroupAverage = computed(() => !userRating.value && !!props.modelValue);
|
||||||
|
const ratingValue = computed(() => (showGroupAverage.value ? props.modelValue : userRating.value) || 0);
|
||||||
const ratingDisplay = computed<Star[]>(
|
const ratingDisplay = computed<Star[]>(
|
||||||
() => {
|
() => {
|
||||||
const stars: Star[] = [];
|
const stars: Star[] = [];
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div @click.prevent>
|
<div @click.prevent>
|
||||||
<!-- User Rating: shows the group average until the user rates the recipe themselves -->
|
|
||||||
<v-rating
|
<v-rating
|
||||||
v-if="isOwnGroup"
|
|
||||||
:model-value="displayRating"
|
:model-value="displayRating"
|
||||||
:active-color="showGroupAverage ? 'grey-darken-1' : 'secondary'"
|
:active-color="showGroupAverage ? 'grey-darken-1' : 'secondary'"
|
||||||
color="secondary-lighten-3"
|
color="secondary-lighten-3"
|
||||||
@@ -10,22 +8,11 @@
|
|||||||
:half-increments="showGroupAverage"
|
:half-increments="showGroupAverage"
|
||||||
:density="small ? 'compact' : 'default'"
|
:density="small ? 'compact' : 'default'"
|
||||||
:size="small ? 'x-small' : undefined"
|
:size="small ? 'x-small' : undefined"
|
||||||
:hover="canHover"
|
:readonly="isReadonly"
|
||||||
:clearable="!!userRatingDisplayValue"
|
:hover="!isReadonly && canHover"
|
||||||
|
:clearable="!!displayRating"
|
||||||
@update:model-value="updateRating(+$event)"
|
@update:model-value="updateRating(+$event)"
|
||||||
/>
|
/>
|
||||||
<!-- Group Rating -->
|
|
||||||
<v-rating
|
|
||||||
v-else
|
|
||||||
:model-value="groupRating"
|
|
||||||
:half-increments="true"
|
|
||||||
active-color="grey-darken-1"
|
|
||||||
color="secondary-lighten-3"
|
|
||||||
length="5"
|
|
||||||
:density="small ? 'compact' : 'default'"
|
|
||||||
:size="small ? 'x-small' : undefined"
|
|
||||||
readonly
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -35,23 +22,23 @@ import { useLoggedInState } from "~/composables/use-logged-in-state";
|
|||||||
import { useUserSelfRatings } from "~/composables/use-users";
|
import { useUserSelfRatings } from "~/composables/use-users";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
emitOnly?: boolean;
|
readonly?: boolean;
|
||||||
recipeId?: string;
|
recipeId?: string;
|
||||||
slug?: string;
|
slug?: string;
|
||||||
small?: boolean;
|
small?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
emitOnly: false,
|
readonly: false,
|
||||||
recipeId: "",
|
recipeId: "",
|
||||||
slug: "",
|
slug: "",
|
||||||
small: false,
|
small: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const modelValue = defineModel<number>({ default: 0 });
|
const groupRating = defineModel<number>({ default: 0 });
|
||||||
const groupRating = computed(() => modelValue.value);
|
|
||||||
|
|
||||||
const { isOwnGroup } = useLoggedInState();
|
const { isOwnGroup } = useLoggedInState();
|
||||||
|
const isReadonly = computed(() => props.readonly || !isOwnGroup.value);
|
||||||
const { userRatings, setRating } = useUserSelfRatings();
|
const { userRatings, setRating } = useUserSelfRatings();
|
||||||
|
|
||||||
// on touch devices a tap fires mouseenter without a matching mouseleave, which leaves v-rating
|
// on touch devices a tap fires mouseenter without a matching mouseleave, which leaves v-rating
|
||||||
@@ -72,19 +59,22 @@ watch(userRating, (value) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const userRatingDisplayValue = computed(() => localUserRating.value ?? 0);
|
// only fall back to the group average when we can't offer the user their own rating,
|
||||||
const showGroupAverage = computed(() => localUserRating.value === null);
|
// and only when there's actually a group average to show. An unset rating may be null or 0
|
||||||
|
const showGroupAverage = computed(() => {
|
||||||
|
return isReadonly.value && !localUserRating.value && !!groupRating.value;
|
||||||
|
});
|
||||||
|
|
||||||
const displayRating = computed(() => {
|
const displayRating = computed(() => {
|
||||||
return showGroupAverage.value ? groupRating.value : userRatingDisplayValue.value;
|
return showGroupAverage.value ? groupRating.value : (localUserRating.value || 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function updateRating(val?: number) {
|
async function updateRating(val?: number) {
|
||||||
if (!isOwnGroup.value) {
|
if (isReadonly.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the group average can be a half value, but user ratings are always whole stars
|
// user ratings are always whole stars
|
||||||
let rating = Math.round(val ?? 0);
|
let rating = Math.round(val ?? 0);
|
||||||
if (rating === localUserRating.value) {
|
if (rating === localUserRating.value) {
|
||||||
rating = 0;
|
rating = 0;
|
||||||
@@ -92,11 +82,6 @@ async function updateRating(val?: number) {
|
|||||||
|
|
||||||
localUserRating.value = rating;
|
localUserRating.value = rating;
|
||||||
|
|
||||||
if (props.emitOnly) {
|
|
||||||
modelValue.value = rating;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingWrites.value++;
|
pendingWrites.value++;
|
||||||
try {
|
try {
|
||||||
await setRating(props.slug, rating, null);
|
await setRating(props.slug, rating, null);
|
||||||
|
|||||||
Reference in New Issue
Block a user