Files
mealie/frontend/app/components/Domain/Recipe/RecipeRating.vue

96 lines
2.6 KiB
Vue
Raw Normal View History

<template>
<div @click.prevent>
<v-rating
:model-value="displayRating"
:active-color="showGroupAverage ? 'grey-darken-1' : 'secondary'"
color="secondary-lighten-3"
length="5"
:half-increments="showGroupAverage"
:density="small ? 'compact' : 'default'"
:size="small ? 'x-small' : undefined"
2026-07-20 15:43:08 -05:00
:readonly="isReadonly"
:hover="!isReadonly && canHover"
:clearable="!!displayRating"
@update:model-value="updateRating(+$event)"
/>
</div>
</template>
<script setup lang="ts">
import { useMediaQuery } from "@vueuse/core";
feat: Remove Explore URLs and make the normal URLs public (#2632) * add groupSlug to most routes * fixed more routing issues * fixed jank and incorrect routes * remove public explore links * remove unused groupSlug and explore routes * nuked explore pages * fixed public toolstore bug * fixed various routes missing group slug * restored public app header menu * fix janky login redirect * 404 recipe API call returns to login * removed unused explore layout * force redirect when using the wrong group slug * fixed dead admin links * removed unused middleware from earlier attempt * 🧹 * improve cookbooks sidebar fixed sidebar link not working fixed sidebar link target hide cookbooks header when there are none * added group slug to user * fix $auth typehints * vastly simplified groupSlug logic * allow logged-in users to view other groups * fixed some edgecases that bypassed isOwnGroup * fixed static home ref * 🧹 * fixed redirect logic * lint warning * removed group slug from group and user pages refactored all components to use route groupSlug or user group slug moved some group pages to recipe pages * fixed some bad types * 🧹 * moved groupSlug routes under /g/groupSlug * move /recipe/ to /r/ * fix backend url generation and metadata injection * moved shopping lists to root/other route fixes * changed shared from /recipes/ to /r/ * fixed 404 redirect not awaiting * removed unused import * fix doc links * fix public recipe setting not affecting public API * fixed backend tests * fix nuxt-generate command --------- Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-11-05 19:07:02 -06:00
import { useLoggedInState } from "~/composables/use-logged-in-state";
import { useUserSelfRatings } from "~/composables/use-users";
interface Props {
2026-07-20 15:43:08 -05:00
readonly?: boolean;
recipeId?: string;
slug?: string;
small?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
2026-07-20 15:43:08 -05:00
readonly: false,
recipeId: "",
slug: "",
small: false,
});
2026-07-20 15:43:08 -05:00
const groupRating = defineModel<number>({ default: 0 });
Use composition API for more components, enable more type checking (#914) * Activate more linting rules from eslint and typescript * Properly add VForm as type information * Fix usage of native types * Fix more linting issues * Rename vuetify types file, add VTooltip * Fix some more typing problems * Use composition API for more components * Convert RecipeRating * Convert RecipeNutrition * Convert more components to composition API * Fix globals plugin for type checking * Add missing icon types * Fix vuetify types in Nuxt context * Use composition API for RecipeActionMenu * Convert error.vue to composition API * Convert RecipeContextMenu to composition API * Use more composition API and type checking in recipe/create * Convert AppButtonUpload to composition API * Fix some type checking in RecipeContextMenu * Remove unused components BaseAutoForm and BaseColorPicker * Convert RecipeCategoryTagDialog to composition API * Convert RecipeCardSection to composition API * Convert RecipeCategoryTagSelector to composition API * Properly import vuetify type definitions * Convert BaseButton to composition API * Convert AutoForm to composition API * Remove unused requests API file * Remove static routes from recipe API * Fix more type errors * Convert AppHeader to composition API, fixing some search bar focus problems * Convert RecipeDialogSearch to composition API * Update API types from pydantic models, handle undefined values * Improve more typing problems * Add types to other plugins * Properly type the CRUD API access * Fix typing of static image routes * Fix more typing stuff * Fix some more typing problems * Turn off more rules
2022-01-09 07:15:23 +01:00
const { isOwnGroup } = useLoggedInState();
2026-07-20 15:43:08 -05:00
const isReadonly = computed(() => props.readonly || !isOwnGroup.value);
const { userRatings, setRating } = useUserSelfRatings();
// on touch devices a tap fires mouseenter without a matching mouseleave, which leaves v-rating
// rendering the stuck hover value instead of the model value
const canHover = useMediaQuery("(hover: hover) and (pointer: fine)");
const userRating = computed<number | null>(() => {
return userRatings.value.find(r => r.recipeId === props.recipeId)?.rating ?? null;
});
const localUserRating = ref(userRating.value);
// while a write is in flight a refetch may still report the pre-click value, so ignore it
const pendingWrites = ref(0);
watch(userRating, (value) => {
if (!pendingWrites.value) {
localUserRating.value = value;
}
});
2026-07-20 15:43:08 -05:00
// only fall back to the group average when we can't offer the user their own rating,
// 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(() => {
2026-07-20 15:43:08 -05:00
return showGroupAverage.value ? groupRating.value : (localUserRating.value || 0);
});
async function updateRating(val?: number) {
2026-07-20 15:43:08 -05:00
if (isReadonly.value) {
return;
}
2026-07-20 15:43:08 -05:00
// user ratings are always whole stars
let rating = Math.round(val ?? 0);
if (rating === localUserRating.value) {
rating = 0;
}
localUserRating.value = rating;
pendingWrites.value++;
try {
await setRating(props.slug, rating, null);
}
finally {
pendingWrites.value--;
}
}
</script>
<style lang="scss" scoped></style>